@weasel-js/registry 1.18.2 → 1.19.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.
- package/dist/index.d.ts +15 -4
- package/dist/index.js +52 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -108,7 +108,8 @@ declare function isDev(): boolean;
|
|
|
108
108
|
|
|
109
109
|
/** Whether `proto` is the prototype a plain object has: `null`, or some
|
|
110
110
|
* realm's `Object.prototype`. Comparing against this realm's alone misses
|
|
111
|
-
* objects made in an iframe, a VM context, or by `structuredClone` under jsdom
|
|
111
|
+
* objects made in an iframe, a VM context, or by `structuredClone` under jsdom;
|
|
112
|
+
* a bare `Object.create(null)` standing in as a prototype is not one. */
|
|
112
113
|
declare function isPlainPrototype(proto: object | null): boolean;
|
|
113
114
|
/** Whether `value` is a plain object — a literal or `Object.create(null)` — from any realm. */
|
|
114
115
|
declare function isPlainObject(value: unknown): value is Record<string, unknown>;
|
|
@@ -132,6 +133,10 @@ declare function deepEqual(a: unknown, b: unknown, opts?: DeepEqualOptions): boo
|
|
|
132
133
|
*/
|
|
133
134
|
declare function sameList<T>(a: readonly T[], b: readonly T[], same?: (x: T, y: T) => boolean): boolean;
|
|
134
135
|
|
|
136
|
+
/** `record[key] = value` as an own property: assignment to `__proto__` would run its setter and change the
|
|
137
|
+
* prototype instead, and a key read from outside may be that one. */
|
|
138
|
+
declare function setOwn(record: Record<string, unknown>, key: string, value: unknown): void;
|
|
139
|
+
|
|
135
140
|
/** How {@link freshId} numbers: from `from` (default 1, also for one that is not finite), the number set off from the base by `sep` (default `'-'`),
|
|
136
141
|
* and with `bare`, the base alone tried first. */
|
|
137
142
|
interface FreshIdOptions {
|
|
@@ -139,11 +144,16 @@ interface FreshIdOptions {
|
|
|
139
144
|
sep?: string;
|
|
140
145
|
bare?: boolean;
|
|
141
146
|
}
|
|
142
|
-
/** The first of `base`, `base-1`, `base-2`, ... (as `opts` shapes them) that `taken` does not hold.
|
|
147
|
+
/** The first of `base`, `base-1`, `base-2`, ... (as `opts` shapes them) that `taken` does not hold. Past 2^53 the
|
|
148
|
+
* count steps to the next number up; a `RangeError` when it would run past the largest. */
|
|
143
149
|
declare function freshId(base: string, taken: {
|
|
144
150
|
has(id: string): boolean;
|
|
145
151
|
}, opts?: FreshIdOptions): string;
|
|
146
152
|
|
|
153
|
+
/** Run `fn` once `ms` have passed, as `setTimeout` would, but waiting out an `ms` longer than one timer holds rather
|
|
154
|
+
* than firing at once, and never for an infinite `ms`. Answers what cancels it. */
|
|
155
|
+
declare function after(ms: number, fn: () => void): () => void;
|
|
156
|
+
|
|
147
157
|
/**
|
|
148
158
|
* The kit-wide "these values disagree" sentinel. Used wherever a value is
|
|
149
159
|
* aggregated across more than one source and the sources don't agree — e.g.
|
|
@@ -174,12 +184,13 @@ type QueryMatch = {
|
|
|
174
184
|
readonly tag: string;
|
|
175
185
|
};
|
|
176
186
|
/** Whether `entry` answers `query`, and by what. Each field matches when it
|
|
177
|
-
* contains the query, ignoring case
|
|
187
|
+
* contains the query, ignoring case and how accented letters are
|
|
188
|
+
* encoded; text is tried before tags. An empty or
|
|
178
189
|
* whitespace query matches everything, by text. */
|
|
179
190
|
declare function matchQuery(query: string, entry: Findable): QueryMatch | null;
|
|
180
191
|
/** Whether `tags` holds `tag` exactly: the test for gathering every entry
|
|
181
192
|
* carrying one, where `matchQuery` is the test for a person's search. */
|
|
182
193
|
declare function hasTag(tags: readonly string[] | undefined, tag: string): boolean;
|
|
183
194
|
|
|
184
|
-
export { MIXED, createEmitter, createReflectable, deepEqual, freshId, hasTag, isDev, isPlainObject, isPlainPrototype, matchQuery, sameList };
|
|
195
|
+
export { MIXED, after, createEmitter, createReflectable, deepEqual, freshId, hasTag, isDev, isPlainObject, isPlainPrototype, matchQuery, sameList, setOwn };
|
|
185
196
|
export type { DeepEqualOptions, Emitter, Findable, FreshIdOptions, Mixed, QueryMatch, Reflectable, ReflectedEntry, Reflection, RegisterOptions, Registrant };
|
package/dist/index.js
CHANGED
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
// src/emitter.ts
|
|
2
2
|
function createEmitter() {
|
|
3
|
-
const listeners = /* @__PURE__ */ new
|
|
3
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
4
4
|
let held = null;
|
|
5
5
|
return {
|
|
6
6
|
subscribe(listener) {
|
|
7
|
-
listeners.
|
|
8
|
-
|
|
7
|
+
let token = listeners.get(listener);
|
|
8
|
+
if (token === void 0) {
|
|
9
|
+
token = {};
|
|
10
|
+
listeners.set(listener, token);
|
|
11
|
+
held = null;
|
|
12
|
+
}
|
|
13
|
+
const mine = token;
|
|
9
14
|
return () => {
|
|
15
|
+
if (listeners.get(listener) !== mine) return;
|
|
10
16
|
listeners.delete(listener);
|
|
11
17
|
held = null;
|
|
12
18
|
};
|
|
13
19
|
},
|
|
14
20
|
emit(...args) {
|
|
15
21
|
if (listeners.size === 0) return;
|
|
16
|
-
for (const listener of held ??= [...listeners]) {
|
|
17
|
-
if (
|
|
22
|
+
for (const [listener, token] of held ??= [...listeners]) {
|
|
23
|
+
if (listeners.get(listener) !== token) continue;
|
|
18
24
|
try {
|
|
19
25
|
listener(...args);
|
|
20
26
|
} catch (err) {
|
|
@@ -115,7 +121,7 @@ function isDev() {
|
|
|
115
121
|
|
|
116
122
|
// src/isPlainObject.ts
|
|
117
123
|
function isPlainPrototype(proto) {
|
|
118
|
-
return proto === Object.prototype || proto === null || Object.getPrototypeOf(proto) === null;
|
|
124
|
+
return proto === Object.prototype || proto === null || Object.getPrototypeOf(proto) === null && Object.hasOwn(proto, "hasOwnProperty") && Object.hasOwn(proto, "isPrototypeOf");
|
|
119
125
|
}
|
|
120
126
|
function isPlainObject(value) {
|
|
121
127
|
return typeof value === "object" && value !== null && isPlainPrototype(Object.getPrototypeOf(value));
|
|
@@ -125,15 +131,20 @@ function isPlainObject(value) {
|
|
|
125
131
|
function deepEqual(a, b, opts = {}) {
|
|
126
132
|
if (Object.is(a, b)) return true;
|
|
127
133
|
if (Array.isArray(a) || Array.isArray(b)) {
|
|
128
|
-
|
|
134
|
+
if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;
|
|
135
|
+
for (let i = 0; i < a.length; i++) if (!deepEqual(a[i], b[i], opts)) return false;
|
|
136
|
+
return true;
|
|
129
137
|
}
|
|
130
138
|
if (!isPlainObject(a) || !isPlainObject(b)) return false;
|
|
131
139
|
if (opts.undefinedAsAbsent) {
|
|
132
|
-
return [.../* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)])].every((k) => deepEqual(a
|
|
140
|
+
return [.../* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)])].every((k) => deepEqual(own(a, k), own(b, k), opts));
|
|
133
141
|
}
|
|
134
142
|
const keys = Object.keys(a);
|
|
135
143
|
return keys.length === Object.keys(b).length && keys.every((k) => Object.hasOwn(b, k) && deepEqual(a[k], b[k], opts));
|
|
136
144
|
}
|
|
145
|
+
function own(o, k) {
|
|
146
|
+
return Object.hasOwn(o, k) ? o[k] : void 0;
|
|
147
|
+
}
|
|
137
148
|
|
|
138
149
|
// src/sameList.ts
|
|
139
150
|
function sameList(a, b, same = (x, y) => x === y) {
|
|
@@ -143,15 +154,40 @@ function sameList(a, b, same = (x, y) => x === y) {
|
|
|
143
154
|
return true;
|
|
144
155
|
}
|
|
145
156
|
|
|
157
|
+
// src/setOwn.ts
|
|
158
|
+
function setOwn(record, key, value) {
|
|
159
|
+
Object.defineProperty(record, key, { value, writable: true, enumerable: true, configurable: true });
|
|
160
|
+
}
|
|
161
|
+
|
|
146
162
|
// src/freshId.ts
|
|
147
163
|
function freshId(base, taken, opts = {}) {
|
|
148
164
|
const { sep = "-", bare = false } = opts;
|
|
149
165
|
const from = opts.from !== void 0 && Number.isFinite(opts.from) ? opts.from : 1;
|
|
150
166
|
if (bare && !taken.has(base)) return base;
|
|
151
167
|
let n = from;
|
|
152
|
-
while (taken.has(`${base}${sep}${n}`)) n
|
|
168
|
+
while (taken.has(`${base}${sep}${n}`)) n = after(n);
|
|
153
169
|
return `${base}${sep}${n}`;
|
|
154
170
|
}
|
|
171
|
+
function after(n) {
|
|
172
|
+
let step = 1;
|
|
173
|
+
while (n + step === n && step < Infinity) step *= 2;
|
|
174
|
+
const next = n + step;
|
|
175
|
+
if (next === Infinity || next === n) throw new RangeError("freshId: no number left past the largest");
|
|
176
|
+
return next;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// src/after.ts
|
|
180
|
+
var LONGEST_TIMER = 2 ** 31 - 1;
|
|
181
|
+
function after2(ms, fn) {
|
|
182
|
+
if (ms === Infinity) return () => {
|
|
183
|
+
};
|
|
184
|
+
let timer;
|
|
185
|
+
const wait = (left) => {
|
|
186
|
+
timer = setTimeout(() => left > LONGEST_TIMER ? wait(left - LONGEST_TIMER) : fn(), Math.min(left, LONGEST_TIMER));
|
|
187
|
+
};
|
|
188
|
+
wait(ms);
|
|
189
|
+
return () => clearTimeout(timer);
|
|
190
|
+
}
|
|
155
191
|
|
|
156
192
|
// src/mixed.ts
|
|
157
193
|
var MIXED = /* @__PURE__ */ Symbol("weasel:mixed");
|
|
@@ -159,16 +195,19 @@ var MIXED = /* @__PURE__ */ Symbol("weasel:mixed");
|
|
|
159
195
|
// src/match.ts
|
|
160
196
|
var BY_TEXT = { by: "text" };
|
|
161
197
|
function matchQuery(query, entry) {
|
|
162
|
-
const q = query.trim()
|
|
198
|
+
const q = fold(query.trim());
|
|
163
199
|
if (q === "") return BY_TEXT;
|
|
164
|
-
if (entry.text.some((t) => t !== void 0 && t
|
|
165
|
-
const tag = entry.tags?.find((t) => t
|
|
200
|
+
if (entry.text.some((t) => t !== void 0 && fold(t).includes(q))) return BY_TEXT;
|
|
201
|
+
const tag = entry.tags?.find((t) => fold(t).includes(q));
|
|
166
202
|
return tag === void 0 ? null : { by: "tag", tag };
|
|
167
203
|
}
|
|
204
|
+
function fold(s) {
|
|
205
|
+
return s.toLowerCase().normalize("NFC").replaceAll("\u03C2", "\u03C3");
|
|
206
|
+
}
|
|
168
207
|
function hasTag(tags, tag) {
|
|
169
208
|
return tags?.includes(tag) ?? false;
|
|
170
209
|
}
|
|
171
210
|
|
|
172
|
-
export { MIXED, createEmitter, createReflectable, deepEqual, freshId, hasTag, isDev, isPlainObject, isPlainPrototype, matchQuery, sameList };
|
|
211
|
+
export { MIXED, after2 as after, createEmitter, createReflectable, deepEqual, freshId, hasTag, isDev, isPlainObject, isPlainPrototype, matchQuery, sameList, setOwn };
|
|
173
212
|
//# sourceMappingURL=index.js.map
|
|
174
213
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/emitter.ts","../src/reflectable.ts","../src/isDev.ts","../src/isPlainObject.ts","../src/deepEqual.ts","../src/sameList.ts","../src/freshId.ts","../src/mixed.ts","../src/match.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/** A set of listeners and the one way the kit notifies them. See {@link createEmitter}. */\nexport interface Emitter<TArgs extends unknown[] = []> {\n /** Add `listener`; a listener already held is held once. Returns the unsubscribe. */\n subscribe(listener: (...args: TArgs) => void): () => void;\n /** Call every listener held when the call began. */\n emit(...args: TArgs): void;\n /** How many listeners are held. */\n readonly size: number;\n}\n\n/**\n * A subscribe/notify pair with stated semantics, for a store to build on\n * instead of keeping its own listener set:\n *\n * - `emit` walks the listeners held when it began, in subscription order. One\n * that subscribes during a pass waits for the next; one unsubscribed before\n * its turn is not called.\n * - A listener that throws does not stop the others, and does not throw into\n * whatever caused the notify: the error goes to `console.error`.\n */\nexport function createEmitter<TArgs extends unknown[] = []>(): Emitter<TArgs> {\n const listeners = new Set<(...args: TArgs) => void>();\n /** `listeners` as an array, rebuilt only after it changes: an emit walks this one. */\n let held: ((...args: TArgs) => void)[] | null = null;\n return {\n subscribe(listener) {\n listeners.add(listener);\n held = null;\n return () => {\n listeners.delete(listener);\n held = null;\n };\n },\n emit(...args) {\n if (listeners.size === 0) return;\n for (const listener of (held ??= [...listeners])) {\n if (!listeners.has(listener)) continue;\n try {\n listener(...args);\n } catch (err) {\n console.error('weasel: a subscriber threw', err);\n }\n }\n },\n get size() {\n return listeners.size;\n },\n };\n}\n","import { createEmitter } from './emitter';\n\n/** One registrant of a key: its value and, when the registrant named one, where it came from. */\nexport interface Registrant<V> {\n readonly value: V;\n readonly source: string | undefined;\n}\n\n/** One key as a reflection reports it. */\nexport interface ReflectedEntry<V, K = string> extends Registrant<V> {\n readonly key: K;\n /** The registrant whose `value` and `source` this entry carries. The same\n * object across snapshots, and the one `shadowed` lists once it is\n * displaced, so it identifies the registrant to {@link Reflectable.release}. */\n readonly live: Registrant<V>;\n /** The registrants this one displaced, oldest first. Non-empty means two\n * registrants claimed one key — a conflict, resolved newest-wins. */\n readonly shadowed: readonly Registrant<V>[];\n}\n\n/**\n * The read-only face of a registry: what is registered, what displaced what,\n * and a way to hear about changes. Every kit registry that embeds a\n * {@link Reflectable} hands this out, so a debug overlay or a conflict check\n * can walk any of them the same way.\n *\n * `subscribe` and `getVersion` are shaped for `useSyncExternalStore`; so is\n * `entries`, which returns the same frozen array until the next change.\n */\nexport interface Reflection<V, K = string> {\n get(key: K): V | undefined;\n has(key: K): boolean;\n /** Every key's live registrant, in the order each key was first registered. */\n entries(): readonly ReflectedEntry<V, K>[];\n /** Called after every change. Returns the unsubscribe. */\n subscribe(listener: () => void): () => void;\n /** Advances on every change. */\n getVersion(): number;\n}\n\n/** Options for {@link Reflectable.push} and {@link Reflectable.set}. */\nexport interface RegisterOptions {\n /** Who registered this — `'kit'`, a package, a plugin id. Reported, never interpreted. */\n source?: string;\n}\n\n/**\n * A keyed store that keeps its own {@link Reflection}. The registry owning it\n * writes through `push` / `set` / `delete` and hands out `reflection`.\n */\nexport interface Reflectable<V, K = string> extends Reflection<V, K> {\n /** Register `value` over whatever `key` holds and return its release.\n * Registrants stack, newest live: releasing the live one uncovers the one\n * it displaced, and releasing a displaced one removes only it. Double-release safe. */\n push(key: K, value: V, options?: RegisterOptions): () => void;\n /** Replace `key`'s live registrant in place, the way `Map.set` does — for\n * registries where a second registration means \"newer version\", not\n * \"override\". Records no conflict. */\n set(key: K, value: V, options?: RegisterOptions): void;\n /** Remove exactly `registrant` — one a reflection reported under `key`, live\n * or shadowed, written by `push` or by `set`. Removing the live one uncovers\n * the one beneath. Returns whether it was there; the release `push` returned\n * for it does nothing afterward, and the reverse. */\n release(key: K, registrant: Registrant<V>): boolean;\n /** Drop every registrant of `key`. Returns whether there was one. */\n delete(key: K): boolean;\n clear(): void;\n /** Announce a change the store cannot see — a registered value mutated in place. */\n bump(): void;\n /** This store as a {@link Reflection}, with no write methods on it. */\n readonly reflection: Reflection<V, K>;\n}\n\n/**\n * Create a keyed store with a uniform reflection surface.\n *\n * Deliberately only the cross-cutting part of a registry: validation, lookup\n * fallbacks and lifecycle stay with the registry that embeds it.\n */\nexport function createReflectable<V, K = string>(): Reflectable<V, K> {\n const stacks = new Map<K, Registrant<V>[]>();\n const listeners = createEmitter();\n let version = 0;\n let snapshot: { version: number; entries: readonly ReflectedEntry<V, K>[] } | null = null;\n\n function changed(): void {\n version++;\n listeners.emit();\n }\n\n const reflection: Reflection<V, K> = {\n get: (key) => stacks.get(key)?.at(-1)?.value,\n has: (key) => stacks.has(key),\n entries() {\n if (snapshot?.version === version) return snapshot.entries;\n const entries: ReflectedEntry<V, K>[] = [];\n for (const [key, stack] of stacks) {\n const live = stack.at(-1)!;\n entries.push(Object.freeze({\n key, live, value: live.value, source: live.source, shadowed: Object.freeze(stack.slice(0, -1)),\n }));\n }\n snapshot = { version, entries: Object.freeze(entries) };\n return snapshot.entries;\n },\n subscribe: listeners.subscribe,\n getVersion: () => version,\n };\n\n function release(key: K, registrant: Registrant<V>): boolean {\n const stack = stacks.get(key);\n const i = stack ? stack.lastIndexOf(registrant) : -1;\n if (i === -1) return false;\n stack!.splice(i, 1);\n if (stack!.length === 0) stacks.delete(key);\n changed();\n return true;\n }\n\n return {\n ...reflection,\n reflection,\n release,\n push(key, value, options) {\n const registrant: Registrant<V> = Object.freeze({ value, source: options?.source });\n const stack = stacks.get(key);\n if (stack) stack.push(registrant);\n else stacks.set(key, [registrant]);\n changed();\n return () => {\n release(key, registrant);\n };\n },\n set(key, value, options) {\n const registrant: Registrant<V> = Object.freeze({ value, source: options?.source });\n const stack = stacks.get(key);\n if (stack) stack[stack.length - 1] = registrant;\n else stacks.set(key, [registrant]);\n changed();\n },\n delete(key) {\n if (!stacks.delete(key)) return false;\n changed();\n return true;\n },\n clear() {\n if (stacks.size === 0) return;\n stacks.clear();\n changed();\n },\n bump: changed,\n };\n}\n","/**\n * Whether to emit development diagnostics. Dev is the default when nothing\n * says otherwise.\n *\n * The read is the bare member expression `process.env.NODE_ENV` because that is\n * the only form a bundler's define replaces; a production build then folds this\n * to `false`. A `typeof process` guard or an optional chain would survive the\n * define and read as dev in every browser. Where nothing defined it (published\n * ESM loaded from a CDN, say) `process` is absent, the read throws, and the\n * answer is dev.\n */\nexport function isDev(): boolean {\n try {\n return process.env.NODE_ENV !== 'production';\n } catch {\n return true;\n }\n}\n","/** Whether `proto` is the prototype a plain object has: `null`, or some\n * realm's `Object.prototype`. Comparing against this realm's alone misses\n * objects made in an iframe, a VM context, or by `structuredClone` under jsdom. */\nexport function isPlainPrototype(proto: object | null): boolean {\n return proto === Object.prototype || proto === null || Object.getPrototypeOf(proto) === null;\n}\n\n/** Whether `value` is a plain object — a literal or `Object.create(null)` — from any realm. */\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && isPlainPrototype(Object.getPrototypeOf(value));\n}\n","import { isPlainObject } from './isPlainObject';\n\n/** Options for {@link deepEqual}. */\nexport interface DeepEqualOptions {\n /** Read a key holding `undefined` the same as one that is absent. Default `false`. */\n undefinedAsAbsent?: boolean;\n}\n\n/**\n * Structural equality over arrays and plain objects from any realm. Every other\n * value — a function, a class instance, a `Date`, a `Map` — equals only itself\n * (`Object.is`).\n */\nexport function deepEqual(a: unknown, b: unknown, opts: DeepEqualOptions = {}): boolean {\n if (Object.is(a, b)) return true;\n if (Array.isArray(a) || Array.isArray(b)) {\n return Array.isArray(a) && Array.isArray(b) && a.length === b.length\n && a.every((v, i) => deepEqual(v, b[i], opts));\n }\n if (!isPlainObject(a) || !isPlainObject(b)) return false;\n if (opts.undefinedAsAbsent) {\n return [...new Set([...Object.keys(a), ...Object.keys(b)])].every((k) => deepEqual(a[k], b[k], opts));\n }\n const keys = Object.keys(a);\n return keys.length === Object.keys(b).length\n && keys.every((k) => Object.hasOwn(b, k) && deepEqual(a[k], b[k], opts));\n}\n","/**\n * Whether `a` and `b` hold equal elements in the same order: the same length,\n * and `same` true at every index. `same` defaults to `===`. Order counts; for\n * two lists holding the same members in any order, compare them as sets.\n */\nexport function sameList<T>(\n a: readonly T[],\n b: readonly T[],\n same: (x: T, y: T) => boolean = (x, y) => x === y,\n): boolean {\n if (a === b) return true;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) if (!same(a[i]!, b[i]!)) return false;\n return true;\n}\n","/** How {@link freshId} numbers: from `from` (default 1, also for one that is not finite), the number set off from the base by `sep` (default `'-'`),\n * and with `bare`, the base alone tried first. */\nexport interface FreshIdOptions {\n from?: number;\n sep?: string;\n bare?: boolean;\n}\n\n/** The first of `base`, `base-1`, `base-2`, ... (as `opts` shapes them) that `taken` does not hold. */\nexport function freshId(base: string, taken: { has(id: string): boolean }, opts: FreshIdOptions = {}): string {\n const { sep = '-', bare = false } = opts;\n const from = opts.from !== undefined && Number.isFinite(opts.from) ? opts.from : 1;\n if (bare && !taken.has(base)) return base;\n let n = from;\n while (taken.has(`${base}${sep}${n}`)) n++;\n return `${base}${sep}${n}`;\n}\n","/**\n * The kit-wide \"these values disagree\" sentinel. Used wherever a value is\n * aggregated across more than one source and the sources don't agree — e.g.\n * a UI panel aggregating a property across a multi-node selection, or text\n * styling aggregated across the runs in a character range. One symbol, one\n * `isMixed` check, regardless of what's being aggregated.\n */\nexport const MIXED: unique symbol = Symbol('weasel:mixed');\n/** The type of {@link MIXED}. Property panels use it to say \"the selection\n * does not agree on this value\" without colliding with any real value. */\nexport type Mixed = typeof MIXED;\n","/**\n * The one text search every registry's finder shares, so a query reaches a\n * command palette's actions and a settings form's prefs the same way.\n */\n\n/** What a search can find an entry by: the words it reads as, and its tags. */\nexport interface Findable {\n /** Label, description, path: whatever the entry is called. Absent fields are skipped. */\n readonly text: readonly (string | undefined)[];\n readonly tags?: readonly string[];\n}\n\n/** Why an entry answered a query: its text, or one of its tags. */\nexport type QueryMatch = { readonly by: 'text' } | { readonly by: 'tag'; readonly tag: string };\n\nconst BY_TEXT: QueryMatch = { by: 'text' };\n\n/** Whether `entry` answers `query`, and by what. Each field matches when it\n * contains the query, ignoring case; text is tried before tags. An empty or\n * whitespace query matches everything, by text. */\nexport function matchQuery(query: string, entry: Findable): QueryMatch | null {\n const q = query.trim().toLowerCase();\n if (q === '') return BY_TEXT;\n if (entry.text.some((t) => t !== undefined && t.toLowerCase().includes(q))) return BY_TEXT;\n const tag = entry.tags?.find((t) => t.toLowerCase().includes(q));\n return tag === undefined ? null : { by: 'tag', tag };\n}\n\n/** Whether `tags` holds `tag` exactly: the test for gathering every entry\n * carrying one, where `matchQuery` is the test for a person's search. */\nexport function hasTag(tags: readonly string[] | undefined, tag: string): boolean {\n return tags?.includes(tag) ?? false;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/emitter.ts","../src/reflectable.ts","../src/isDev.ts","../src/isPlainObject.ts","../src/deepEqual.ts","../src/sameList.ts","../src/setOwn.ts","../src/freshId.ts","../src/after.ts","../src/mixed.ts","../src/match.ts"],"names":["after"],"mappings":"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","file":"index.js","sourcesContent":["/** A set of listeners and the one way the kit notifies them. See {@link createEmitter}. */\nexport interface Emitter<TArgs extends unknown[] = []> {\n /** Add `listener`; a listener already held is held once. Returns the unsubscribe. */\n subscribe(listener: (...args: TArgs) => void): () => void;\n /** Call every listener held when the call began. */\n emit(...args: TArgs): void;\n /** How many listeners are held. */\n readonly size: number;\n}\n\n/**\n * A subscribe/notify pair with stated semantics, for a store to build on\n * instead of keeping its own listener set:\n *\n * - `emit` walks the listeners held when it began, in subscription order. One\n * that subscribes during a pass waits for the next; one unsubscribed before\n * its turn is not called.\n * - A listener that throws does not stop the others, and does not throw into\n * whatever caused the notify: the error goes to `console.error`.\n */\nexport function createEmitter<TArgs extends unknown[] = []>(): Emitter<TArgs> {\n type Listener = (...args: TArgs) => void;\n /** Each listener held, by the subscription holding it: a token that a pass and an unsubscribe check against. */\n const listeners = new Map<Listener, object>();\n /** `listeners` as an array, rebuilt only after it changes: an emit walks this one. */\n let held: (readonly [Listener, object])[] | null = null;\n return {\n subscribe(listener) {\n let token = listeners.get(listener);\n if (token === undefined) {\n token = {};\n listeners.set(listener, token);\n held = null;\n }\n const mine = token;\n return () => {\n if (listeners.get(listener) !== mine) return;\n listeners.delete(listener);\n held = null;\n };\n },\n emit(...args) {\n if (listeners.size === 0) return;\n for (const [listener, token] of (held ??= [...listeners])) {\n if (listeners.get(listener) !== token) continue;\n try {\n listener(...args);\n } catch (err) {\n console.error('weasel: a subscriber threw', err);\n }\n }\n },\n get size() {\n return listeners.size;\n },\n };\n}\n","import { createEmitter } from './emitter';\n\n/** One registrant of a key: its value and, when the registrant named one, where it came from. */\nexport interface Registrant<V> {\n readonly value: V;\n readonly source: string | undefined;\n}\n\n/** One key as a reflection reports it. */\nexport interface ReflectedEntry<V, K = string> extends Registrant<V> {\n readonly key: K;\n /** The registrant whose `value` and `source` this entry carries. The same\n * object across snapshots, and the one `shadowed` lists once it is\n * displaced, so it identifies the registrant to {@link Reflectable.release}. */\n readonly live: Registrant<V>;\n /** The registrants this one displaced, oldest first. Non-empty means two\n * registrants claimed one key — a conflict, resolved newest-wins. */\n readonly shadowed: readonly Registrant<V>[];\n}\n\n/**\n * The read-only face of a registry: what is registered, what displaced what,\n * and a way to hear about changes. Every kit registry that embeds a\n * {@link Reflectable} hands this out, so a debug overlay or a conflict check\n * can walk any of them the same way.\n *\n * `subscribe` and `getVersion` are shaped for `useSyncExternalStore`; so is\n * `entries`, which returns the same frozen array until the next change.\n */\nexport interface Reflection<V, K = string> {\n get(key: K): V | undefined;\n has(key: K): boolean;\n /** Every key's live registrant, in the order each key was first registered. */\n entries(): readonly ReflectedEntry<V, K>[];\n /** Called after every change. Returns the unsubscribe. */\n subscribe(listener: () => void): () => void;\n /** Advances on every change. */\n getVersion(): number;\n}\n\n/** Options for {@link Reflectable.push} and {@link Reflectable.set}. */\nexport interface RegisterOptions {\n /** Who registered this — `'kit'`, a package, a plugin id. Reported, never interpreted. */\n source?: string;\n}\n\n/**\n * A keyed store that keeps its own {@link Reflection}. The registry owning it\n * writes through `push` / `set` / `delete` and hands out `reflection`.\n */\nexport interface Reflectable<V, K = string> extends Reflection<V, K> {\n /** Register `value` over whatever `key` holds and return its release.\n * Registrants stack, newest live: releasing the live one uncovers the one\n * it displaced, and releasing a displaced one removes only it. Double-release safe. */\n push(key: K, value: V, options?: RegisterOptions): () => void;\n /** Replace `key`'s live registrant in place, the way `Map.set` does — for\n * registries where a second registration means \"newer version\", not\n * \"override\". Records no conflict. */\n set(key: K, value: V, options?: RegisterOptions): void;\n /** Remove exactly `registrant` — one a reflection reported under `key`, live\n * or shadowed, written by `push` or by `set`. Removing the live one uncovers\n * the one beneath. Returns whether it was there; the release `push` returned\n * for it does nothing afterward, and the reverse. */\n release(key: K, registrant: Registrant<V>): boolean;\n /** Drop every registrant of `key`. Returns whether there was one. */\n delete(key: K): boolean;\n clear(): void;\n /** Announce a change the store cannot see — a registered value mutated in place. */\n bump(): void;\n /** This store as a {@link Reflection}, with no write methods on it. */\n readonly reflection: Reflection<V, K>;\n}\n\n/**\n * Create a keyed store with a uniform reflection surface.\n *\n * Deliberately only the cross-cutting part of a registry: validation, lookup\n * fallbacks and lifecycle stay with the registry that embeds it.\n */\nexport function createReflectable<V, K = string>(): Reflectable<V, K> {\n const stacks = new Map<K, Registrant<V>[]>();\n const listeners = createEmitter();\n let version = 0;\n let snapshot: { version: number; entries: readonly ReflectedEntry<V, K>[] } | null = null;\n\n function changed(): void {\n version++;\n listeners.emit();\n }\n\n const reflection: Reflection<V, K> = {\n get: (key) => stacks.get(key)?.at(-1)?.value,\n has: (key) => stacks.has(key),\n entries() {\n if (snapshot?.version === version) return snapshot.entries;\n const entries: ReflectedEntry<V, K>[] = [];\n for (const [key, stack] of stacks) {\n const live = stack.at(-1)!;\n entries.push(Object.freeze({\n key, live, value: live.value, source: live.source, shadowed: Object.freeze(stack.slice(0, -1)),\n }));\n }\n snapshot = { version, entries: Object.freeze(entries) };\n return snapshot.entries;\n },\n subscribe: listeners.subscribe,\n getVersion: () => version,\n };\n\n function release(key: K, registrant: Registrant<V>): boolean {\n const stack = stacks.get(key);\n const i = stack ? stack.lastIndexOf(registrant) : -1;\n if (i === -1) return false;\n stack!.splice(i, 1);\n if (stack!.length === 0) stacks.delete(key);\n changed();\n return true;\n }\n\n return {\n ...reflection,\n reflection,\n release,\n push(key, value, options) {\n const registrant: Registrant<V> = Object.freeze({ value, source: options?.source });\n const stack = stacks.get(key);\n if (stack) stack.push(registrant);\n else stacks.set(key, [registrant]);\n changed();\n return () => {\n release(key, registrant);\n };\n },\n set(key, value, options) {\n const registrant: Registrant<V> = Object.freeze({ value, source: options?.source });\n const stack = stacks.get(key);\n if (stack) stack[stack.length - 1] = registrant;\n else stacks.set(key, [registrant]);\n changed();\n },\n delete(key) {\n if (!stacks.delete(key)) return false;\n changed();\n return true;\n },\n clear() {\n if (stacks.size === 0) return;\n stacks.clear();\n changed();\n },\n bump: changed,\n };\n}\n","/**\n * Whether to emit development diagnostics. Dev is the default when nothing\n * says otherwise.\n *\n * The read is the bare member expression `process.env.NODE_ENV` because that is\n * the only form a bundler's define replaces; a production build then folds this\n * to `false`. A `typeof process` guard or an optional chain would survive the\n * define and read as dev in every browser. Where nothing defined it (published\n * ESM loaded from a CDN, say) `process` is absent, the read throws, and the\n * answer is dev.\n */\nexport function isDev(): boolean {\n try {\n return process.env.NODE_ENV !== 'production';\n } catch {\n return true;\n }\n}\n","/** Whether `proto` is the prototype a plain object has: `null`, or some\n * realm's `Object.prototype`. Comparing against this realm's alone misses\n * objects made in an iframe, a VM context, or by `structuredClone` under jsdom;\n * a bare `Object.create(null)` standing in as a prototype is not one. */\nexport function isPlainPrototype(proto: object | null): boolean {\n return proto === Object.prototype || proto === null\n || (Object.getPrototypeOf(proto) === null && Object.hasOwn(proto, 'hasOwnProperty') && Object.hasOwn(proto, 'isPrototypeOf'));\n}\n\n/** Whether `value` is a plain object — a literal or `Object.create(null)` — from any realm. */\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && isPlainPrototype(Object.getPrototypeOf(value));\n}\n","import { isPlainObject } from './isPlainObject';\n\n/** Options for {@link deepEqual}. */\nexport interface DeepEqualOptions {\n /** Read a key holding `undefined` the same as one that is absent. Default `false`. */\n undefinedAsAbsent?: boolean;\n}\n\n/**\n * Structural equality over arrays and plain objects from any realm. Every other\n * value — a function, a class instance, a `Date`, a `Map` — equals only itself\n * (`Object.is`).\n */\nexport function deepEqual(a: unknown, b: unknown, opts: DeepEqualOptions = {}): boolean {\n if (Object.is(a, b)) return true;\n if (Array.isArray(a) || Array.isArray(b)) {\n if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) if (!deepEqual(a[i], b[i], opts)) return false;\n return true;\n }\n if (!isPlainObject(a) || !isPlainObject(b)) return false;\n if (opts.undefinedAsAbsent) {\n return [...new Set([...Object.keys(a), ...Object.keys(b)])].every((k) => deepEqual(own(a, k), own(b, k), opts));\n }\n const keys = Object.keys(a);\n return keys.length === Object.keys(b).length\n && keys.every((k) => Object.hasOwn(b, k) && deepEqual(a[k], b[k], opts));\n}\n\n/** `o`'s own `k`: a key named like an `Object.prototype` member is read as a plain own key. */\nfunction own(o: Record<string, unknown>, k: string): unknown {\n return Object.hasOwn(o, k) ? o[k] : undefined;\n}\n","/**\n * Whether `a` and `b` hold equal elements in the same order: the same length,\n * and `same` true at every index. `same` defaults to `===`. Order counts; for\n * two lists holding the same members in any order, compare them as sets.\n */\nexport function sameList<T>(\n a: readonly T[],\n b: readonly T[],\n same: (x: T, y: T) => boolean = (x, y) => x === y,\n): boolean {\n if (a === b) return true;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) if (!same(a[i]!, b[i]!)) return false;\n return true;\n}\n","/** `record[key] = value` as an own property: assignment to `__proto__` would run its setter and change the\n * prototype instead, and a key read from outside may be that one. */\nexport function setOwn(record: Record<string, unknown>, key: string, value: unknown): void {\n Object.defineProperty(record, key, { value, writable: true, enumerable: true, configurable: true });\n}\n","/** How {@link freshId} numbers: from `from` (default 1, also for one that is not finite), the number set off from the base by `sep` (default `'-'`),\n * and with `bare`, the base alone tried first. */\nexport interface FreshIdOptions {\n from?: number;\n sep?: string;\n bare?: boolean;\n}\n\n/** The first of `base`, `base-1`, `base-2`, ... (as `opts` shapes them) that `taken` does not hold. Past 2^53 the\n * count steps to the next number up; a `RangeError` when it would run past the largest. */\nexport function freshId(base: string, taken: { has(id: string): boolean }, opts: FreshIdOptions = {}): string {\n const { sep = '-', bare = false } = opts;\n const from = opts.from !== undefined && Number.isFinite(opts.from) ? opts.from : 1;\n if (bare && !taken.has(base)) return base;\n let n = from;\n while (taken.has(`${base}${sep}${n}`)) n = after(n);\n return `${base}${sep}${n}`;\n}\n\n/** `n + 1`, or past 2^53, where that rounds back to `n`, the next number up. */\nfunction after(n: number): number {\n let step = 1;\n while (n + step === n && step < Infinity) step *= 2;\n const next = n + step;\n if (next === Infinity || next === n) throw new RangeError('freshId: no number left past the largest');\n return next;\n}\n","/** The longest wait one `setTimeout` keeps; past it, a timer fires at once. */\nconst LONGEST_TIMER = 2 ** 31 - 1;\n\n/** Run `fn` once `ms` have passed, as `setTimeout` would, but waiting out an `ms` longer than one timer holds rather\n * than firing at once, and never for an infinite `ms`. Answers what cancels it. */\nexport function after(ms: number, fn: () => void): () => void {\n if (ms === Infinity) return () => {};\n let timer: ReturnType<typeof setTimeout>;\n const wait = (left: number): void => {\n timer = setTimeout(() => (left > LONGEST_TIMER ? wait(left - LONGEST_TIMER) : fn()), Math.min(left, LONGEST_TIMER));\n };\n wait(ms);\n return () => clearTimeout(timer);\n}\n","/**\n * The kit-wide \"these values disagree\" sentinel. Used wherever a value is\n * aggregated across more than one source and the sources don't agree — e.g.\n * a UI panel aggregating a property across a multi-node selection, or text\n * styling aggregated across the runs in a character range. One symbol, one\n * `isMixed` check, regardless of what's being aggregated.\n */\nexport const MIXED: unique symbol = Symbol('weasel:mixed');\n/** The type of {@link MIXED}. Property panels use it to say \"the selection\n * does not agree on this value\" without colliding with any real value. */\nexport type Mixed = typeof MIXED;\n","/**\n * The one text search every registry's finder shares, so a query reaches a\n * command palette's actions and a settings form's prefs the same way.\n */\n\n/** What a search can find an entry by: the words it reads as, and its tags. */\nexport interface Findable {\n /** Label, description, path: whatever the entry is called. Absent fields are skipped. */\n readonly text: readonly (string | undefined)[];\n readonly tags?: readonly string[];\n}\n\n/** Why an entry answered a query: its text, or one of its tags. */\nexport type QueryMatch = { readonly by: 'text' } | { readonly by: 'tag'; readonly tag: string };\n\nconst BY_TEXT: QueryMatch = { by: 'text' };\n\n/** Whether `entry` answers `query`, and by what. Each field matches when it\n * contains the query, ignoring case and how accented letters are\n * encoded; text is tried before tags. An empty or\n * whitespace query matches everything, by text. */\nexport function matchQuery(query: string, entry: Findable): QueryMatch | null {\n const q = fold(query.trim());\n if (q === '') return BY_TEXT;\n if (entry.text.some((t) => t !== undefined && fold(t).includes(q))) return BY_TEXT;\n const tag = entry.tags?.find((t) => fold(t).includes(q));\n return tag === undefined ? null : { by: 'tag', tag };\n}\n\n/** `s` lowercased and composed, with final sigma folded to `σ`: the one letter `toLowerCase` spells by its place in\n * a word, so a text holding the query exactly could otherwise miss it. Composing reads `é` and `e` + U+0301 alike. */\nfunction fold(s: string): string {\n return s.toLowerCase().normalize('NFC').replaceAll('ς', 'σ');\n}\n\n/** Whether `tags` holds `tag` exactly: the test for gathering every entry\n * carrying one, where `matchQuery` is the test for a person's search. */\nexport function hasTag(tags: readonly string[] | undefined, tag: string): boolean {\n return tags?.includes(tag) ?? false;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weasel-js/registry",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.19.0",
|
|
4
4
|
"description": "Keyed registries with a uniform reflection surface: enumerate the entries, see which ones displaced others, release any one of them, and subscribe to changes. Also the small shared pieces registries are built from: an emitter, a plain-object test, and the MIXED sentinel. No React, no DOM.",
|
|
5
5
|
"weasel": {
|
|
6
6
|
"tier": "base"
|