@warp-drive/core 5.7.0-alpha.0 → 5.7.0-alpha.1

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.
@@ -1761,6 +1761,19 @@ export interface ObjectSchema {
1761
1761
  objectExtensions?: string[];
1762
1762
  }
1763
1763
  export type Schema = ResourceSchema | ObjectSchema;
1764
+ export interface PolarisTrait {
1765
+ name: string;
1766
+ mode: "polaris";
1767
+ fields: PolarisModeFieldSchema[];
1768
+ traits?: string[];
1769
+ }
1770
+ export interface LegacyTrait {
1771
+ name: string;
1772
+ mode: "legacy";
1773
+ fields: LegacyModeFieldSchema[];
1774
+ traits?: string[];
1775
+ }
1776
+ export type Trait = LegacyTrait | PolarisTrait;
1764
1777
  /**
1765
1778
  * A no-op type utility that enables type-checking resource schema
1766
1779
  * definitions.
@@ -6,6 +6,7 @@
6
6
  */
7
7
  export type { StableRecordIdentifier, ResourceKey } from "./types/identifier.js";
8
8
  export type { CacheCapabilitiesManager } from "./store/-types/q/cache-capabilities-manager.js";
9
- export type { ModelSchema } from "./store/-types/q/ds-model.js";
9
+ // FIXME this should come from somewhere more intelligent
10
+ export type { ModelSchema } from "./store/deprecated/-private.js";
10
11
  export type { SchemaService } from "./store/-types/q/schema-service.js";
11
12
  export type { BaseFinderOptions, FindRecordOptions, LegacyResourceQuery, QueryOptions, FindAllOptions } from "./store/-types/q/store.js";
@@ -1,5 +1,5 @@
1
1
  import { deprecate, warn } from '@ember/debug';
2
- import { p as peekCache } from "../request-state-CjLph1LP.js";
2
+ import { p as peekCache } from "../request-state-CejVJgdj.js";
3
3
  import '../types/request.js';
4
4
  import { macroCondition, getGlobalConfig } from '@embroider/macros';
5
5
  import '../utils/string.js';
@@ -1,4 +1,4 @@
1
- import { J as ReactiveDocument } from "./request-state-CjLph1LP.js";
1
+ import { K as ReactiveDocument } from "./request-state-CejVJgdj.js";
2
2
  import { SkipCache, EnableHydration } from './types/request.js';
3
3
  import { macroCondition, getGlobalConfig } from '@embroider/macros';
4
4
  const MUTATION_OPS = new Set(['createRecord', 'updateRecord', 'deleteRecord']);
package/dist/index.js CHANGED
@@ -3,8 +3,8 @@ import { a as cloneResponseProperties, I as IS_CACHE_HANDLER, b as assertValidRe
3
3
  import { macroCondition, getGlobalConfig } from '@embroider/macros';
4
4
  import { w as waitFor } from "./configure-B48bFHOl.js";
5
5
  import { peekUniversalTransient, setUniversalTransient } from './types/-private.js';
6
- export { S as Store, r as recordIdentifierFor, N as setIdentifierForgetMethod, K as setIdentifierGenerationMethod, O as setIdentifierResetMethod, L as setIdentifierUpdateMethod, P as setKeyInfoForResource, s as storeFor } from "./request-state-CjLph1LP.js";
7
- export { C as CacheHandler } from "./handler-C2T-IyJK.js";
6
+ export { S as Store, r as recordIdentifierFor, O as setIdentifierForgetMethod, L as setIdentifierGenerationMethod, P as setIdentifierResetMethod, N as setIdentifierUpdateMethod, Q as setKeyInfoForResource, s as storeFor } from "./request-state-CejVJgdj.js";
7
+ export { C as CacheHandler } from "./handler-D2jjnIA-.js";
8
8
  import '@ember/debug';
9
9
  import './utils/string.js';
10
10
 
package/dist/reactive.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { isResourceSchema } from './types/schema/fields.js';
2
- import { E as withSignalStore, Q as isExtensionProp, T as performExtensionSet, G as consumeInternalSignal, U as performArrayExtensionGet, x as entangleSignal, V as performObjectExtensionGet, d as SOURCE$1, f as fastPush, y as defineSignal, l as RelatedCollection, H as getOrCreateInternalSignal, F as notifyInternalSignal, A as Signals, h as setRecordIdentifier, r as recordIdentifierFor } from "./request-state-CjLph1LP.js";
2
+ import { F as withSignalStore, T as isExtensionProp, U as performExtensionSet, H as consumeInternalSignal, V as performArrayExtensionGet, x as entangleSignal, W as performObjectExtensionGet, d as SOURCE$1, f as fastPush, y as defineSignal, l as RelatedCollection, J as getOrCreateInternalSignal, G as notifyInternalSignal, B as Signals, h as setRecordIdentifier, r as recordIdentifierFor } from "./request-state-CejVJgdj.js";
3
3
  import { EnableHydration, STRUCTURED } from './types/request.js';
4
4
  import { macroCondition, getGlobalConfig } from '@embroider/macros';
5
- import { deprecate } from '@ember/debug';
5
+ import { warn, deprecate } from '@ember/debug';
6
6
  import './utils/string.js';
7
7
  import { A as ARRAY_SIGNAL, O as OBJECT_SIGNAL, c as createMemo } from "./configure-B48bFHOl.js";
8
8
  import { RecordStore, Type } from './types/symbols.js';
@@ -1724,7 +1724,7 @@ class SchemaService {
1724
1724
  this._transforms = new Map();
1725
1725
  this._hashFns = new Map();
1726
1726
  this._derivations = new Map();
1727
- this._traits = new Set();
1727
+ this._traits = new Map();
1728
1728
  this._modes = new Map();
1729
1729
  this._extensions = {
1730
1730
  object: new Map(),
@@ -1821,7 +1821,7 @@ class SchemaService {
1821
1821
  const fields = new Map();
1822
1822
  const relationships = {};
1823
1823
  const attributes = {};
1824
- schema.fields.forEach(field => {
1824
+ for (const field of schema.fields) {
1825
1825
  macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1826
1826
  if (!test) {
1827
1827
  throw new Error(`${field.kind} is not valid inside a ResourceSchema's fields.`);
@@ -1835,13 +1835,12 @@ class SchemaService {
1835
1835
  } else if (field.kind === 'belongsTo' || field.kind === 'hasMany') {
1836
1836
  relationships[field.name] = field;
1837
1837
  }
1838
- });
1838
+ }
1839
1839
  const traits = new Set(isResourceSchema(schema) ? schema.traits : []);
1840
- traits.forEach(trait => {
1841
- this._traits.add(trait);
1842
- });
1840
+ const finalized = traits.size === 0;
1843
1841
  const internalSchema = {
1844
1842
  original: schema,
1843
+ finalized,
1845
1844
  fields,
1846
1845
  relationships,
1847
1846
  attributes,
@@ -1849,6 +1848,44 @@ class SchemaService {
1849
1848
  };
1850
1849
  this._schemas.set(schema.type, internalSchema);
1851
1850
  }
1851
+
1852
+ /**
1853
+ * Registers a {@link Trait} for use by resource schemas.
1854
+ *
1855
+ * Traits are re-usable collections of fields that can be composed to
1856
+ * build up a resource schema. Often they represent polymorphic behaviors
1857
+ * a resource should exhibit.
1858
+ *
1859
+ * When we finalize a resource, we walk its traits and apply their fields
1860
+ * to the resource's fields. All specified traits must be registered by
1861
+ * this time or an error will be thrown.
1862
+ *
1863
+ * Traits are applied left-to-right, with traits of traits being applied in the same
1864
+ * way. Thus for the most part, application of traits is a post-order graph traversal
1865
+ * problem.
1866
+ *
1867
+ * A trait is only ever processed once. If multiple traits (A, B, C) have the same
1868
+ * trait (D) as a dependency, D will be included only once when first encountered by
1869
+ * A.
1870
+ *
1871
+ * If a cycle exists such that trait A has trait B which has Trait A, trait A will
1872
+ * be applied *after* trait B in production. In development a cycle error will be thrown.
1873
+ *
1874
+ * Fields are finalized on a "last wins principle". Thus traits appearing higher in
1875
+ * the tree and further to the right of a traits array take precedence, with the
1876
+ * resource's fields always being applied last and winning out.
1877
+ *
1878
+ * @public
1879
+ */
1880
+ registerTrait(trait) {
1881
+ const internalTrait = Object.assign({}, trait, {
1882
+ fields: new Map()
1883
+ });
1884
+ for (const field of trait.fields) {
1885
+ internalTrait.fields.set(field.name, field);
1886
+ }
1887
+ this._traits.set(trait.name, internalTrait);
1888
+ }
1852
1889
  registerTransformation(transformation) {
1853
1890
  this._transforms.set(transformation[Type], transformation);
1854
1891
  }
@@ -1873,6 +1910,9 @@ class SchemaService {
1873
1910
  CAUTION_MEGA_DANGER_ZONE_arrayExtensions(field) {
1874
1911
  return processExtensions(this, field, 'array');
1875
1912
  }
1913
+ CAUTION_MEGA_DANGER_ZONE_hasExtension(ext) {
1914
+ return this._extensions[ext.kind].has(ext.name);
1915
+ }
1876
1916
 
1877
1917
  /**
1878
1918
  * This is an internal method used to register behaviors for legacy mode.
@@ -1926,8 +1966,13 @@ class SchemaService {
1926
1966
  type
1927
1967
  }) {
1928
1968
  const schema = this._schemas.get(type);
1929
- if (!schema) {
1930
- throw new Error(`No schema defined for ${type}`);
1969
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
1970
+ if (!test) {
1971
+ throw new Error(`No schema defined for ${type}`);
1972
+ }
1973
+ })(schema) : {};
1974
+ if (!schema.finalized) {
1975
+ finalizeResource(this, schema);
1931
1976
  }
1932
1977
  return schema.fields;
1933
1978
  }
@@ -1985,4 +2030,84 @@ if (macroCondition(getGlobalConfig().WarpDrive.deprecations.ENABLE_LEGACY_SCHEMA
1985
2030
  return this._schemas.has(type);
1986
2031
  };
1987
2032
  }
2033
+
2034
+ /**
2035
+ * When we finalize a resource, we walk its traits and apply their fields
2036
+ * to the resource's fields.
2037
+ *
2038
+ * Traits are applied left-to-right, with traits of traits being applied in the same
2039
+ * way. Thus for the most part, application of traits is a post-order graph traversal
2040
+ * problem.
2041
+ *
2042
+ * A trait is only ever processed once. If multiple traits (A, B, C) have the same
2043
+ * trait (D) as a dependency, D will be included only once when first encountered by
2044
+ * A.
2045
+ *
2046
+ * If a cycle exists such that trait A has trait B which has Trait A, trait A will
2047
+ * be applied *after* trait B in production. In development a cycle error will be thrown.
2048
+ *
2049
+ * Fields are finalized on a "last wins principle". Thus traits appearing higher in
2050
+ * the tree and further to the right of a traits array take precedence, with the
2051
+ * resource's fields always being applied last and winning out.
2052
+ */
2053
+ function finalizeResource(schema, resource) {
2054
+ const fields = new Map();
2055
+ const seen = new Set();
2056
+ for (const traitName of resource.traits) {
2057
+ const trait = schema._traits.get(traitName);
2058
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
2059
+ if (!test) {
2060
+ throw new Error(`The trait ${traitName} MUST be supplied before the resource ${resource.original.type} can be finalized for use.`);
2061
+ }
2062
+ })(trait) : {};
2063
+ walkTrait(schema, trait, fields, seen, resource.original.type, macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? [] : null);
2064
+ }
2065
+ mergeMap(fields, resource.fields);
2066
+ resource.fields = fields;
2067
+ resource.finalized = true;
2068
+ }
2069
+ function walkTrait(schema, trait, fields, seen, type, debugPath) {
2070
+ if (seen.has(trait)) {
2071
+ // if the trait is in the current path, we throw a cycle error in dev.
2072
+ if (macroCondition(getGlobalConfig().WarpDrive.env.DEBUG)) {
2073
+ if (debugPath.includes(trait.name)) {
2074
+ throw new Error(`CycleError: The Trait '${trait.name}' utilized by the Resource '${type}' includes the following circular reference "${debugPath.join(' > ')} > ${trait.name}"`);
2075
+ }
2076
+ }
2077
+ return;
2078
+ }
2079
+ const ownPath = macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? [...debugPath, trait.name] : null;
2080
+
2081
+ // immediately mark as seen to prevent cycles
2082
+ // further down the tree from looping back
2083
+ seen.add(trait);
2084
+
2085
+ // first apply any child traits
2086
+ if (trait.traits?.length) {
2087
+ for (const traitName of trait.traits) {
2088
+ const subtrait = schema._traits.get(traitName);
2089
+ if (macroCondition(getGlobalConfig().WarpDrive.features.ENFORCE_STRICT_RESOURCE_FINALIZATION)) {
2090
+ macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
2091
+ if (!test) {
2092
+ throw new Error(`The trait ${traitName} used by the trait ${trait.name} MUST be supplied before the resource ${type} can be finalized for use.`);
2093
+ }
2094
+ })(subtrait) : {};
2095
+ } else {
2096
+ warn(`The trait ${traitName} used by the trait ${trait.name} MUST be supplied before the resource ${type} can be finalized for use.`, !!subtrait, {
2097
+ id: 'warp-drive:missing-trait-schema-for-resource'
2098
+ });
2099
+ }
2100
+ if (!subtrait) continue;
2101
+ walkTrait(schema, subtrait, fields, seen, type, ownPath);
2102
+ }
2103
+ }
2104
+
2105
+ // then apply our own fields
2106
+ mergeMap(fields, trait.fields);
2107
+ }
2108
+ function mergeMap(base, toApply) {
2109
+ for (const [key, value] of toApply) {
2110
+ base.set(key, value);
2111
+ }
2112
+ }
1988
2113
  export { Checkout, SchemaService, fromIdentity, instantiateRecord, registerDerivations, teardownRecord, withDefaults };