Comparing version 1.0.9 to 2.0.0
declare const INTERNAL: unique symbol; | ||
declare const MIID_DEBUG: unique symbol; | ||
declare const PROVIDER: unique symbol; | ||
declare const PARENT: unique symbol; | ||
interface KeyConsumer<T, HasDefault extends boolean = boolean> { | ||
@@ -32,22 +34,57 @@ readonly name: string; | ||
}): Key<T, false>; | ||
type StaackInternal<Parent extends Staack = Staack> = { | ||
readonly provider: KeyProvider<any>; | ||
readonly parent: Parent; | ||
}; | ||
declare class MissingContextError extends Error { | ||
context: KeyConsumer<any>; | ||
keyConsumer: KeyConsumer<any>; | ||
readonly help?: string; | ||
constructor(context: KeyConsumer<any>); | ||
constructor(keyConsumer: KeyConsumer<any>); | ||
} | ||
declare class Staack { | ||
type StaackCoreTuple = [parent: StaackCore, provider: KeyProvider<any>]; | ||
type StaackCoreValue = StaackCore | null; | ||
declare class StaackCore { | ||
static readonly MissingContextError: typeof MissingContextError; | ||
static applyKeys<T extends Staack>(instance: T, keys: Array<KeyProvider<any>>, instantiate: (internal: StaackInternal<T>) => T): T; | ||
private readonly [INTERNAL]; | ||
constructor(internal?: StaackInternal<Staack> | null); | ||
protected readInternal(consumer: KeyConsumer<any, any>): { | ||
private readonly [PARENT]; | ||
private readonly [PROVIDER]; | ||
protected constructor(provider: KeyProvider<any>, parent?: StaackCoreValue); | ||
/** | ||
* READ Functions | ||
*/ | ||
static findFirstMatch(staack: StaackCoreValue, consumer: KeyConsumer<any, any>): { | ||
found: boolean; | ||
value: any; | ||
}; | ||
has(ctx: KeyConsumer<any, any>): boolean; | ||
get<T, HasDefault extends boolean>(ctx: KeyConsumer<T, HasDefault>): HasDefault extends true ? T : T | null; | ||
static has(staack: StaackCoreValue, consumer: KeyConsumer<any, any>): boolean; | ||
static get<T, HasDefault extends boolean>(staack: StaackCoreValue, consumer: KeyConsumer<T, HasDefault>): HasDefault extends true ? T : T | null; | ||
static getAll<T>(staack: StaackCoreValue, consumer: KeyConsumer<T>): IterableIterator<T>; | ||
static getOrFail<T>(staack: StaackCoreValue, consumer: KeyConsumer<T>): T; | ||
static extract(staack: StaackCoreValue): IterableIterator<StaackCoreTuple>; | ||
/** | ||
* WRITE Functions | ||
*/ | ||
static with(staack: StaackCoreValue, ...keys: readonly KeyProvider<any>[]): StaackCoreValue; | ||
/** | ||
* Merge two StaackCore instances into one. | ||
* [...left, ...right] | ||
* If left is empty, return right. | ||
* If right is empty, return left. | ||
*/ | ||
static merge(left: StaackCoreValue, right: StaackCoreValue): StaackCoreValue; | ||
/** | ||
* Remove duplicated providers from the StaackCore. | ||
*/ | ||
static dedupe(staack: StaackCoreValue): StaackCoreValue; | ||
static debug(staack: StaackCoreValue): Array<{ | ||
value: any; | ||
ctxId: string; | ||
}>; | ||
} | ||
declare class Staack { | ||
static readonly MissingContextError: typeof MissingContextError; | ||
private readonly [INTERNAL]; | ||
protected constructor(core?: StaackCoreValue); | ||
static create(...keys: Array<KeyProvider<any>>): Staack; | ||
has(consumer: KeyConsumer<any, any>): boolean; | ||
get<T, HasDefault extends boolean>(consumer: KeyConsumer<T, HasDefault>): HasDefault extends true ? T : T | null; | ||
getAll<T>(consumer: KeyConsumer<T>): IterableIterator<T>; | ||
getOrFail<T>(consumer: KeyConsumer<T>): T; | ||
@@ -58,5 +95,8 @@ debug(): Array<{ | ||
}>; | ||
with(...keys: Array<KeyProvider<any>>): Staack; | ||
protected instantiate(staackCore: StaackCoreValue): this; | ||
with(...keys: Array<KeyProvider<any>>): this; | ||
merge(other: Staack): this; | ||
dedupe(): this; | ||
} | ||
export { INTERNAL, Key, KeyConsumer, KeyProvider, KeyProviderFn, MIID_DEBUG, MissingContextError, Staack, StaackInternal, createKey }; | ||
export { Key, KeyConsumer, KeyProvider, KeyProviderFn, MissingContextError, Staack, StaackCore, StaackCoreTuple, StaackCoreValue, createKey }; |
252
dist/mod.js
@@ -28,11 +28,16 @@ "use strict"; | ||
__export(mod_exports, { | ||
INTERNAL: () => INTERNAL, | ||
MIID_DEBUG: () => MIID_DEBUG, | ||
MissingContextError: () => MissingContextError, | ||
Staack: () => Staack, | ||
StaackCore: () => StaackCore, | ||
createKey: () => createKey | ||
}); | ||
module.exports = __toCommonJS(mod_exports); | ||
var INTERNAL = Symbol.for("MIID_INTERNAL"); | ||
var MIID_DEBUG = Symbol.for("MIID_DEBUG"); | ||
// src/constants.ts | ||
var INTERNAL = Symbol.for("INTERNAL"); | ||
var PROVIDER = Symbol.for("PROVIDER"); | ||
var PARENT = Symbol.for("PARENT"); | ||
var DEBUG = Symbol.for("DEBUG"); | ||
// src/Key.ts | ||
function createKey(options) { | ||
@@ -53,48 +58,47 @@ const { help, name } = options; | ||
} | ||
// src/MissingContextError.ts | ||
var MissingContextError = class extends Error { | ||
constructor(context) { | ||
super(`Cannot find context ${context.name}`); | ||
this.context = context; | ||
constructor(keyConsumer) { | ||
super(`Cannot find context ${keyConsumer.name}`); | ||
this.keyConsumer = keyConsumer; | ||
Object.setPrototypeOf(this, new.target.prototype); | ||
this.help = context[INTERNAL].help; | ||
this.help = keyConsumer[INTERNAL].help; | ||
} | ||
help; | ||
}; | ||
var _Staack = class { | ||
static applyKeys(instance, keys, instantiate) { | ||
if (keys.length === 0) { | ||
return instance; | ||
} | ||
return [...keys].reverse().reduce((parent, provider) => { | ||
return instantiate({ parent, provider }); | ||
}, instance); | ||
// src/StaackCore.ts | ||
var _StaackCore = class { | ||
[PARENT]; | ||
// Null if root | ||
[PROVIDER]; | ||
constructor(provider, parent = null) { | ||
this[PARENT] = parent; | ||
this[PROVIDER] = provider; | ||
} | ||
[INTERNAL]; | ||
constructor(internal = null) { | ||
this[INTERNAL] = internal; | ||
} | ||
readInternal(consumer) { | ||
const internal = this[INTERNAL]; | ||
if (internal === null) { | ||
return { | ||
found: false, | ||
value: null | ||
}; | ||
/** | ||
* READ Functions | ||
*/ | ||
static findFirstMatch(staack, consumer) { | ||
if (staack === null) { | ||
return { found: false, value: null }; | ||
} | ||
if (internal.provider[INTERNAL].consumer === consumer) { | ||
const provider = staack[PROVIDER]; | ||
if (provider[INTERNAL].consumer === consumer) { | ||
return { | ||
found: true, | ||
value: internal.provider[INTERNAL].value | ||
value: provider[INTERNAL].value | ||
}; | ||
} | ||
return internal.parent.readInternal(consumer); | ||
return _StaackCore.findFirstMatch(staack[PARENT], consumer); | ||
} | ||
has(ctx) { | ||
return this.readInternal(ctx).found; | ||
static has(staack, consumer) { | ||
return _StaackCore.findFirstMatch(staack, consumer).found; | ||
} | ||
get(ctx) { | ||
const res = this.readInternal(ctx); | ||
static get(staack, consumer) { | ||
const res = _StaackCore.findFirstMatch(staack, consumer); | ||
if (res.found === false) { | ||
if (ctx[INTERNAL].hasDefault) { | ||
return ctx[INTERNAL].defaultValue; | ||
if (consumer[INTERNAL].hasDefault) { | ||
return consumer[INTERNAL].defaultValue; | ||
} | ||
@@ -105,4 +109,22 @@ return null; | ||
} | ||
getOrFail(consumer) { | ||
const res = this.readInternal(consumer); | ||
static getAll(staack, consumer) { | ||
let current = staack; | ||
return { | ||
next() { | ||
while (current) { | ||
const provider = current[PROVIDER]; | ||
current = current[PARENT]; | ||
if (provider[INTERNAL].consumer === consumer) { | ||
return { value: provider[INTERNAL].value, done: false }; | ||
} | ||
} | ||
return { value: void 0, done: true }; | ||
}, | ||
[Symbol.iterator]() { | ||
return this; | ||
} | ||
}; | ||
} | ||
static getOrFail(staack, consumer) { | ||
const res = _StaackCore.findFirstMatch(staack, consumer); | ||
if (res.found === false) { | ||
@@ -116,33 +138,148 @@ if (consumer[INTERNAL].hasDefault) { | ||
} | ||
debug() { | ||
static extract(staack) { | ||
let current = staack; | ||
return { | ||
next() { | ||
if (current) { | ||
const parent = current; | ||
const provider = current[PROVIDER]; | ||
current = current[PARENT]; | ||
return { value: [parent, provider], done: false }; | ||
} | ||
return { value: void 0, done: true }; | ||
}, | ||
[Symbol.iterator]() { | ||
return this; | ||
} | ||
}; | ||
} | ||
/** | ||
* WRITE Functions | ||
*/ | ||
static with(staack, ...keys) { | ||
if (keys.length === 0) { | ||
return staack; | ||
} | ||
return [...keys].reduce((parent, provider) => { | ||
return new _StaackCore(provider, parent); | ||
}, staack); | ||
} | ||
/** | ||
* Merge two StaackCore instances into one. | ||
* [...left, ...right] | ||
* If left is empty, return right. | ||
* If right is empty, return left. | ||
*/ | ||
static merge(left, right) { | ||
if (left === null || right === null) { | ||
return left ?? right ?? null; | ||
} | ||
const rightExtracted = Array.from(_StaackCore.extract(right), ([, provider]) => provider).reverse(); | ||
return _StaackCore.with(left, ...rightExtracted); | ||
} | ||
/** | ||
* Remove duplicated providers from the StaackCore. | ||
*/ | ||
static dedupe(staack) { | ||
if (staack === null) { | ||
return null; | ||
} | ||
const seenKeys = /* @__PURE__ */ new Set(); | ||
const queue = []; | ||
let base = staack; | ||
let baseQueue = []; | ||
for (const [item, provider] of _StaackCore.extract(staack)) { | ||
if (seenKeys.has(provider[INTERNAL].consumer)) { | ||
base = item[PARENT]; | ||
queue.push(...baseQueue); | ||
baseQueue = []; | ||
continue; | ||
} | ||
seenKeys.add(provider[INTERNAL].consumer); | ||
baseQueue.push(provider); | ||
} | ||
if (base === staack) { | ||
return staack; | ||
} | ||
queue.push(...baseQueue); | ||
return _StaackCore.with(base, ...queue.reverse()); | ||
} | ||
static debug(staack) { | ||
const world = globalThis; | ||
const idMap = world[MIID_DEBUG] || /* @__PURE__ */ new Map(); | ||
if (!world[MIID_DEBUG]) { | ||
world[MIID_DEBUG] = idMap; | ||
} | ||
const idMap = world[DEBUG] || (world[DEBUG] = /* @__PURE__ */ new Map()); | ||
const result = []; | ||
const traverse = (staack) => { | ||
const internal = staack[INTERNAL]; | ||
if (internal === null) { | ||
traverse(staack); | ||
return result; | ||
function traverse(staack2) { | ||
if (staack2 === null) { | ||
return; | ||
} | ||
let ctxId = idMap.get(internal.provider[INTERNAL].consumer); | ||
const provider = staack2[PROVIDER]; | ||
let ctxId = idMap.get(provider[INTERNAL].consumer); | ||
if (ctxId === void 0) { | ||
ctxId = Math.random().toString(36).substring(7); | ||
idMap.set(internal.provider[INTERNAL].consumer, ctxId); | ||
idMap.set(provider[INTERNAL].consumer, ctxId); | ||
} | ||
result.push({ | ||
ctxId, | ||
value: internal.provider[INTERNAL].value | ||
value: provider[INTERNAL].value | ||
}); | ||
if (internal.parent) { | ||
traverse(internal.parent); | ||
} | ||
}; | ||
traverse(this); | ||
return result; | ||
traverse(staack2[PARENT]); | ||
} | ||
} | ||
}; | ||
var StaackCore = _StaackCore; | ||
__publicField(StaackCore, "MissingContextError", MissingContextError); | ||
// src/Staack.ts | ||
var _Staack = class { | ||
[INTERNAL]; | ||
constructor(core = null) { | ||
this[INTERNAL] = core; | ||
} | ||
static create(...keys) { | ||
return new _Staack(StaackCore.with(null, ...keys)); | ||
} | ||
has(consumer) { | ||
return StaackCore.has(this[INTERNAL], consumer); | ||
} | ||
get(consumer) { | ||
return StaackCore.get(this[INTERNAL], consumer); | ||
} | ||
getAll(consumer) { | ||
return StaackCore.getAll(this[INTERNAL], consumer); | ||
} | ||
getOrFail(consumer) { | ||
return StaackCore.getOrFail(this[INTERNAL], consumer); | ||
} | ||
debug() { | ||
return StaackCore.debug(this[INTERNAL]); | ||
} | ||
instantiate(staackCore) { | ||
if (this.constructor !== _Staack) { | ||
throw new Error("Cannot instantiate a Staack subclass, you need to override instantiate()"); | ||
} | ||
return new _Staack(staackCore); | ||
} | ||
with(...keys) { | ||
return _Staack.applyKeys(this, keys, (internal) => new _Staack(internal)); | ||
const nextCore = StaackCore.with(this[INTERNAL], ...keys); | ||
if (nextCore === this[INTERNAL]) { | ||
return this; | ||
} | ||
return this.instantiate(nextCore); | ||
} | ||
merge(other) { | ||
const nextCore = StaackCore.merge(this[INTERNAL], other[INTERNAL]); | ||
if (nextCore === this[INTERNAL]) { | ||
return this; | ||
} | ||
return this.instantiate(nextCore); | ||
} | ||
dedupe() { | ||
const nextCore = StaackCore.dedupe(this[INTERNAL]); | ||
if (nextCore === this[INTERNAL]) { | ||
return this; | ||
} | ||
return this.instantiate(nextCore); | ||
} | ||
}; | ||
@@ -153,7 +290,6 @@ var Staack = _Staack; | ||
0 && (module.exports = { | ||
INTERNAL, | ||
MIID_DEBUG, | ||
MissingContextError, | ||
Staack, | ||
StaackCore, | ||
createKey | ||
}); |
{ | ||
"name": "staack", | ||
"version": "1.0.9", | ||
"version": "2.0.0", | ||
"description": "A library to create type-safe opaque stacks", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -8,3 +8,13 @@ # 🏯 Staack | ||
```ts | ||
// TODO | ||
// 1. Create a key with a name and a type | ||
const NumKey = createKey<number>({ name: 'Num' }); | ||
// 2. Create a stack | ||
const stack = Staack.create(); | ||
// 3. Add a value to the stack using the key (Staack is immutable, it returns a new instance) | ||
const stack2 = stack.with(NumKey.Provider(42)); | ||
// 4. Get the value from the stack using the key | ||
expect(stack2.get(NumKey.Consumer)).toBe(42); | ||
``` | ||
@@ -26,32 +36,16 @@ | ||
class CustomStaack extends Staack { | ||
// You need to override the `with` method to return a new instance of your CustomStaack | ||
with(...keys: Array<KeyProvider<any>>): CustomStaack { | ||
// Use the static `applyKeys` method to apply keys to the current instance | ||
return Staack.applyKeys<CustomStaack>(this, keys, (internal) => new CustomStaack(internal)); | ||
// Override the `create` method to return a new instance of your CustomStack | ||
static create(...keys: KeyProvider<any, boolean>[]): CustomStaack { | ||
return new CustomStaack().with(...keys); | ||
} | ||
} | ||
const custom = new CustomStaack(); | ||
expect(custom instanceof CustomStaack).toBe(true); | ||
expect(custom instanceof Staack).toBe(true); | ||
``` | ||
If you want to pass custom arguments to yout CustomStaack: | ||
```ts | ||
class ParamsStaack extends Staack { | ||
// You can pass your own parameters to the constructor | ||
constructor(public readonly param: string, internal: StaackInternal<ParamsStaack> | null = null) { | ||
super(internal); | ||
// You need to override the `instantiate` method to return a new instance of your CustomStack | ||
protected instantiate(staackCore: StaackCoreValue): this { | ||
return new CustomStaack(staackCore) as any; | ||
} | ||
with(...keys: Array<KeyProvider<any>>): ParamsStaack { | ||
return Staack.applyKeys<ParamsStaack>(this, keys, (internal) => new ParamsStaack(this.param, internal)); | ||
} | ||
} | ||
const custom = new ParamsStaack('some value'); | ||
expect(custom.param).toBe('some value'); | ||
expect(custom instanceof ParamsStaack).toBe(true); | ||
const custom = CustomStaack.create(); | ||
expect(custom instanceof CustomStaack).toBe(true); | ||
expect(custom instanceof Staack).toBe(true); | ||
``` |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
23248
636
50