@matter/general 0.16.0-alpha.0-20251203-aaf94bca8 → 0.16.0-alpha.0-20251207-37e501b18
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/cjs/MatterError.js +2 -2
- package/dist/cjs/MatterError.js.map +1 -1
- package/dist/cjs/codec/DerTypes.d.ts.map +1 -1
- package/dist/cjs/codec/DerTypes.js +2 -1
- package/dist/cjs/codec/DerTypes.js.map +1 -1
- package/dist/cjs/environment/Environment.d.ts +4 -1
- package/dist/cjs/environment/Environment.d.ts.map +1 -1
- package/dist/cjs/environment/Environment.js +83 -3
- package/dist/cjs/environment/Environment.js.map +1 -1
- package/dist/cjs/environment/RuntimeService.d.ts +3 -44
- package/dist/cjs/environment/RuntimeService.d.ts.map +1 -1
- package/dist/cjs/environment/RuntimeService.js +65 -10
- package/dist/cjs/environment/RuntimeService.js.map +2 -2
- package/dist/cjs/environment/Worker.d.ts +56 -0
- package/dist/cjs/environment/Worker.d.ts.map +1 -0
- package/dist/cjs/environment/Worker.js +44 -0
- package/dist/cjs/environment/Worker.js.map +6 -0
- package/dist/cjs/environment/index.d.ts +1 -0
- package/dist/cjs/environment/index.d.ts.map +1 -1
- package/dist/cjs/environment/index.js +1 -0
- package/dist/cjs/environment/index.js.map +1 -1
- package/dist/cjs/log/Diagnostic.d.ts +11 -1
- package/dist/cjs/log/Diagnostic.d.ts.map +1 -1
- package/dist/cjs/log/Diagnostic.js +3 -0
- package/dist/cjs/log/Diagnostic.js.map +1 -1
- package/dist/cjs/log/DiagnosticPresentation.d.ts +4 -0
- package/dist/cjs/log/DiagnosticPresentation.d.ts.map +1 -1
- package/dist/cjs/log/DiagnosticPresentation.js +3 -2
- package/dist/cjs/log/DiagnosticPresentation.js.map +1 -1
- package/dist/cjs/log/DiagnosticSource.d.ts +1 -1
- package/dist/cjs/log/DiagnosticSource.d.ts.map +1 -1
- package/dist/cjs/log/DiagnosticSource.js +5 -1
- package/dist/cjs/log/DiagnosticSource.js.map +1 -1
- package/dist/cjs/log/LogFormat.js +2 -2
- package/dist/cjs/log/LogFormat.js.map +1 -1
- package/dist/cjs/net/Channel.d.ts +1 -1
- package/dist/cjs/net/Channel.d.ts.map +1 -1
- package/dist/cjs/net/Channel.js +1 -1
- package/dist/cjs/net/Channel.js.map +1 -1
- package/dist/cjs/net/mock/MockUdpChannel.d.ts +1 -1
- package/dist/cjs/net/mock/MockUdpChannel.d.ts.map +1 -1
- package/dist/cjs/net/mock/NetworkSimulator.d.ts.map +1 -1
- package/dist/cjs/net/mock/NetworkSimulator.js +2 -1
- package/dist/cjs/net/mock/NetworkSimulator.js.map +1 -1
- package/dist/cjs/net/udp/UdpChannel.d.ts +5 -1
- package/dist/cjs/net/udp/UdpChannel.d.ts.map +1 -1
- package/dist/cjs/net/udp/UdpMulticastServer.d.ts +4 -6
- package/dist/cjs/net/udp/UdpMulticastServer.d.ts.map +1 -1
- package/dist/cjs/net/udp/UdpMulticastServer.js +114 -37
- package/dist/cjs/net/udp/UdpMulticastServer.js.map +1 -1
- package/dist/cjs/time/StandardTime.d.ts +38 -0
- package/dist/cjs/time/StandardTime.d.ts.map +1 -0
- package/dist/cjs/time/StandardTime.js +172 -0
- package/dist/cjs/time/StandardTime.js.map +6 -0
- package/dist/cjs/time/Time.d.ts +4 -23
- package/dist/cjs/time/Time.d.ts.map +1 -1
- package/dist/cjs/time/Time.js +12 -88
- package/dist/cjs/time/Time.js.map +1 -1
- package/dist/cjs/time/Timestamp.d.ts +5 -0
- package/dist/cjs/time/Timestamp.d.ts.map +1 -1
- package/dist/cjs/time/Timestamp.js +4 -0
- package/dist/cjs/time/Timestamp.js.map +1 -1
- package/dist/cjs/time/index.d.ts +1 -0
- package/dist/cjs/time/index.d.ts.map +1 -1
- package/dist/cjs/time/index.js +1 -0
- package/dist/cjs/time/index.js.map +1 -1
- package/dist/cjs/transaction/Transaction.d.ts +12 -4
- package/dist/cjs/transaction/Transaction.d.ts.map +1 -1
- package/dist/cjs/transaction/Transaction.js +4 -4
- package/dist/cjs/transaction/Transaction.js.map +1 -1
- package/dist/cjs/transaction/Tx.d.ts +2 -1
- package/dist/cjs/transaction/Tx.d.ts.map +1 -1
- package/dist/cjs/transaction/Tx.js +92 -6
- package/dist/cjs/transaction/Tx.js.map +1 -1
- package/dist/cjs/util/Abort.js +1 -1
- package/dist/cjs/util/Abort.js.map +1 -1
- package/dist/cjs/util/Construction.d.ts +2 -1
- package/dist/cjs/util/Construction.d.ts.map +1 -1
- package/dist/cjs/util/Construction.js +60 -16
- package/dist/cjs/util/Construction.js.map +2 -2
- package/dist/cjs/util/DataReadQueue.d.ts +1 -5
- package/dist/cjs/util/DataReadQueue.d.ts.map +1 -1
- package/dist/cjs/util/DataReadQueue.js +12 -11
- package/dist/cjs/util/DataReadQueue.js.map +1 -1
- package/dist/cjs/util/FormattedText.d.ts +1 -1
- package/dist/cjs/util/FormattedText.d.ts.map +1 -1
- package/dist/cjs/util/FormattedText.js +1 -3
- package/dist/cjs/util/FormattedText.js.map +1 -1
- package/dist/cjs/util/Lifetime.d.ts +97 -0
- package/dist/cjs/util/Lifetime.d.ts.map +1 -0
- package/dist/cjs/util/Lifetime.js +166 -0
- package/dist/cjs/util/Lifetime.js.map +6 -0
- package/dist/cjs/util/Multiplex.js +2 -2
- package/dist/cjs/util/Multiplex.js.map +1 -1
- package/dist/cjs/util/Observable.d.ts +27 -26
- package/dist/cjs/util/Observable.d.ts.map +1 -1
- package/dist/cjs/util/Observable.js +76 -35
- package/dist/cjs/util/Observable.js.map +2 -2
- package/dist/cjs/util/String.d.ts +5 -29
- package/dist/cjs/util/String.d.ts.map +1 -1
- package/dist/cjs/util/String.js +16 -158
- package/dist/cjs/util/String.js.map +2 -2
- package/dist/cjs/util/identifier-case.d.ts +19 -0
- package/dist/cjs/util/identifier-case.d.ts.map +1 -0
- package/dist/cjs/util/identifier-case.js +99 -0
- package/dist/cjs/util/identifier-case.js.map +6 -0
- package/dist/cjs/util/index.d.ts +3 -0
- package/dist/cjs/util/index.d.ts.map +1 -1
- package/dist/cjs/util/index.js +3 -0
- package/dist/cjs/util/index.js.map +1 -1
- package/dist/cjs/util/serialize.d.ts +25 -0
- package/dist/cjs/util/serialize.d.ts.map +1 -0
- package/dist/cjs/util/serialize.js +116 -0
- package/dist/cjs/util/serialize.js.map +6 -0
- package/dist/esm/MatterError.js +1 -1
- package/dist/esm/codec/DerTypes.d.ts.map +1 -1
- package/dist/esm/codec/DerTypes.js +2 -1
- package/dist/esm/codec/DerTypes.js.map +1 -1
- package/dist/esm/environment/Environment.d.ts +4 -1
- package/dist/esm/environment/Environment.d.ts.map +1 -1
- package/dist/esm/environment/Environment.js +83 -3
- package/dist/esm/environment/Environment.js.map +1 -1
- package/dist/esm/environment/RuntimeService.d.ts +3 -44
- package/dist/esm/environment/RuntimeService.d.ts.map +1 -1
- package/dist/esm/environment/RuntimeService.js +65 -10
- package/dist/esm/environment/RuntimeService.js.map +2 -2
- package/dist/esm/environment/Worker.d.ts +56 -0
- package/dist/esm/environment/Worker.d.ts.map +1 -0
- package/dist/esm/environment/Worker.js +24 -0
- package/dist/esm/environment/Worker.js.map +6 -0
- package/dist/esm/environment/index.d.ts +1 -0
- package/dist/esm/environment/index.d.ts.map +1 -1
- package/dist/esm/environment/index.js +1 -0
- package/dist/esm/environment/index.js.map +1 -1
- package/dist/esm/log/Diagnostic.d.ts +11 -1
- package/dist/esm/log/Diagnostic.d.ts.map +1 -1
- package/dist/esm/log/Diagnostic.js +3 -0
- package/dist/esm/log/Diagnostic.js.map +1 -1
- package/dist/esm/log/DiagnosticPresentation.d.ts +4 -0
- package/dist/esm/log/DiagnosticPresentation.d.ts.map +1 -1
- package/dist/esm/log/DiagnosticPresentation.js +3 -2
- package/dist/esm/log/DiagnosticPresentation.js.map +1 -1
- package/dist/esm/log/DiagnosticSource.d.ts +1 -1
- package/dist/esm/log/DiagnosticSource.d.ts.map +1 -1
- package/dist/esm/log/DiagnosticSource.js +5 -1
- package/dist/esm/log/DiagnosticSource.js.map +1 -1
- package/dist/esm/log/LogFormat.js +1 -1
- package/dist/esm/log/LogFormat.js.map +1 -1
- package/dist/esm/net/Channel.d.ts +1 -1
- package/dist/esm/net/Channel.d.ts.map +1 -1
- package/dist/esm/net/Channel.js +1 -1
- package/dist/esm/net/Channel.js.map +1 -1
- package/dist/esm/net/mock/MockUdpChannel.d.ts +1 -1
- package/dist/esm/net/mock/MockUdpChannel.d.ts.map +1 -1
- package/dist/esm/net/mock/NetworkSimulator.d.ts.map +1 -1
- package/dist/esm/net/mock/NetworkSimulator.js +2 -1
- package/dist/esm/net/mock/NetworkSimulator.js.map +1 -1
- package/dist/esm/net/udp/UdpChannel.d.ts +5 -1
- package/dist/esm/net/udp/UdpChannel.d.ts.map +1 -1
- package/dist/esm/net/udp/UdpMulticastServer.d.ts +4 -6
- package/dist/esm/net/udp/UdpMulticastServer.d.ts.map +1 -1
- package/dist/esm/net/udp/UdpMulticastServer.js +114 -37
- package/dist/esm/net/udp/UdpMulticastServer.js.map +1 -1
- package/dist/esm/time/StandardTime.d.ts +38 -0
- package/dist/esm/time/StandardTime.d.ts.map +1 -0
- package/dist/esm/time/StandardTime.js +152 -0
- package/dist/esm/time/StandardTime.js.map +6 -0
- package/dist/esm/time/Time.d.ts +4 -23
- package/dist/esm/time/Time.d.ts.map +1 -1
- package/dist/esm/time/Time.js +12 -88
- package/dist/esm/time/Time.js.map +1 -1
- package/dist/esm/time/Timestamp.d.ts +5 -0
- package/dist/esm/time/Timestamp.d.ts.map +1 -1
- package/dist/esm/time/Timestamp.js +4 -0
- package/dist/esm/time/Timestamp.js.map +1 -1
- package/dist/esm/time/index.d.ts +1 -0
- package/dist/esm/time/index.d.ts.map +1 -1
- package/dist/esm/time/index.js +1 -0
- package/dist/esm/time/index.js.map +1 -1
- package/dist/esm/transaction/Transaction.d.ts +12 -4
- package/dist/esm/transaction/Transaction.d.ts.map +1 -1
- package/dist/esm/transaction/Transaction.js +4 -4
- package/dist/esm/transaction/Transaction.js.map +1 -1
- package/dist/esm/transaction/Tx.d.ts +2 -1
- package/dist/esm/transaction/Tx.d.ts.map +1 -1
- package/dist/esm/transaction/Tx.js +92 -6
- package/dist/esm/transaction/Tx.js.map +1 -1
- package/dist/esm/util/Abort.js +1 -1
- package/dist/esm/util/Abort.js.map +1 -1
- package/dist/esm/util/Construction.d.ts +2 -1
- package/dist/esm/util/Construction.d.ts.map +1 -1
- package/dist/esm/util/Construction.js +60 -16
- package/dist/esm/util/Construction.js.map +2 -2
- package/dist/esm/util/DataReadQueue.d.ts +1 -5
- package/dist/esm/util/DataReadQueue.d.ts.map +1 -1
- package/dist/esm/util/DataReadQueue.js +12 -11
- package/dist/esm/util/DataReadQueue.js.map +1 -1
- package/dist/esm/util/FormattedText.d.ts +1 -1
- package/dist/esm/util/FormattedText.d.ts.map +1 -1
- package/dist/esm/util/FormattedText.js +2 -4
- package/dist/esm/util/FormattedText.js.map +1 -1
- package/dist/esm/util/Lifetime.d.ts +97 -0
- package/dist/esm/util/Lifetime.d.ts.map +1 -0
- package/dist/esm/util/Lifetime.js +146 -0
- package/dist/esm/util/Lifetime.js.map +6 -0
- package/dist/esm/util/Multiplex.js +2 -2
- package/dist/esm/util/Multiplex.js.map +1 -1
- package/dist/esm/util/Observable.d.ts +27 -26
- package/dist/esm/util/Observable.d.ts.map +1 -1
- package/dist/esm/util/Observable.js +76 -35
- package/dist/esm/util/Observable.js.map +2 -2
- package/dist/esm/util/String.d.ts +5 -29
- package/dist/esm/util/String.d.ts.map +1 -1
- package/dist/esm/util/String.js +16 -158
- package/dist/esm/util/String.js.map +2 -2
- package/dist/esm/util/identifier-case.d.ts +19 -0
- package/dist/esm/util/identifier-case.d.ts.map +1 -0
- package/dist/esm/util/identifier-case.js +79 -0
- package/dist/esm/util/identifier-case.js.map +6 -0
- package/dist/esm/util/index.d.ts +3 -0
- package/dist/esm/util/index.d.ts.map +1 -1
- package/dist/esm/util/index.js +3 -0
- package/dist/esm/util/index.js.map +1 -1
- package/dist/esm/util/serialize.d.ts +25 -0
- package/dist/esm/util/serialize.d.ts.map +1 -0
- package/dist/esm/util/serialize.js +96 -0
- package/dist/esm/util/serialize.js.map +6 -0
- package/package.json +2 -2
- package/src/MatterError.ts +1 -1
- package/src/codec/DerTypes.ts +2 -1
- package/src/environment/Environment.ts +29 -4
- package/src/environment/RuntimeService.ts +19 -55
- package/src/environment/Worker.ts +75 -0
- package/src/environment/index.ts +1 -0
- package/src/log/Diagnostic.ts +15 -1
- package/src/log/DiagnosticPresentation.ts +7 -2
- package/src/log/DiagnosticSource.ts +5 -1
- package/src/log/LogFormat.ts +1 -1
- package/src/net/Channel.ts +2 -2
- package/src/net/mock/MockUdpChannel.ts +1 -1
- package/src/net/mock/NetworkSimulator.ts +2 -1
- package/src/net/udp/UdpChannel.ts +6 -1
- package/src/net/udp/UdpMulticastServer.ts +72 -34
- package/src/time/StandardTime.ts +122 -0
- package/src/time/Time.ts +13 -105
- package/src/time/Timestamp.ts +8 -0
- package/src/time/index.ts +1 -0
- package/src/transaction/Transaction.ts +19 -6
- package/src/transaction/Tx.ts +49 -2
- package/src/util/Abort.ts +1 -1
- package/src/util/Construction.ts +76 -18
- package/src/util/DataReadQueue.ts +14 -12
- package/src/util/FormattedText.ts +1 -1
- package/src/util/Lifetime.ts +284 -0
- package/src/util/Multiplex.ts +2 -2
- package/src/util/Observable.ts +132 -53
- package/src/util/String.ts +14 -215
- package/src/util/identifier-case.ts +101 -0
- package/src/util/index.ts +3 -0
- package/src/util/serialize.ts +129 -0
package/src/util/Observable.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { Diagnostic } from "#log/Diagnostic.js";
|
|
7
8
|
import { Duration } from "#time/Duration.js";
|
|
8
9
|
import { Time, Timer } from "#time/Time.js";
|
|
9
10
|
import { Instant, Millis, Seconds } from "#time/TimeUnit.js";
|
|
@@ -30,10 +31,12 @@ const logger = Logger.get("Observable");
|
|
|
30
31
|
* @param payload a list of arguments to be emitted
|
|
31
32
|
*/
|
|
32
33
|
export interface Observer<T extends any[] = any[], R = void> {
|
|
33
|
-
(...payload: T):
|
|
34
|
+
(...payload: T): R | undefined;
|
|
34
35
|
[observant]?: boolean;
|
|
35
36
|
}
|
|
36
37
|
|
|
38
|
+
export interface AsyncObserver<T extends any[] = any[], R = void> extends Observer<T, MaybePromise<R>> {}
|
|
39
|
+
|
|
37
40
|
/**
|
|
38
41
|
* A discrete event that may be monitored via callback. Could call it "event" but that could be confused with Matter
|
|
39
42
|
* cluster events and/or DOM events.
|
|
@@ -113,6 +116,11 @@ export interface Observable<T extends any[] = any[], R = void> extends AsyncIter
|
|
|
113
116
|
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null,
|
|
114
117
|
): Promise<TResult1 | TResult2>;
|
|
115
118
|
|
|
119
|
+
/**
|
|
120
|
+
* A diagnostic aid; set this to produce detailed logs of emission.
|
|
121
|
+
*/
|
|
122
|
+
traceAs: string | undefined;
|
|
123
|
+
|
|
116
124
|
/**
|
|
117
125
|
* Observable supports standard "for await (const value of observable").
|
|
118
126
|
*
|
|
@@ -193,6 +201,7 @@ export class BasicObservable<T extends any[] = any[], R = void> implements Obser
|
|
|
193
201
|
#handlePromise!: ObserverPromiseHandler;
|
|
194
202
|
#observers?: Set<Observer<T, R>>;
|
|
195
203
|
#once?: Set<Observer<T, R>>;
|
|
204
|
+
#instrumentAs?: string;
|
|
196
205
|
|
|
197
206
|
#joinIteration?: () => Promise<Next<T>>;
|
|
198
207
|
#removeIterator?: () => void;
|
|
@@ -207,6 +216,10 @@ export class BasicObservable<T extends any[] = any[], R = void> implements Obser
|
|
|
207
216
|
}
|
|
208
217
|
}
|
|
209
218
|
|
|
219
|
+
set traceAs(name: string | undefined) {
|
|
220
|
+
this.#instrumentAs = name;
|
|
221
|
+
}
|
|
222
|
+
|
|
210
223
|
[Symbol.dispose]() {
|
|
211
224
|
this.#observers = this.#once = undefined;
|
|
212
225
|
|
|
@@ -227,7 +240,8 @@ export class BasicObservable<T extends any[] = any[], R = void> implements Obser
|
|
|
227
240
|
// Promises handled by emitter
|
|
228
241
|
this.#handlePromise = promise => promise;
|
|
229
242
|
} else {
|
|
230
|
-
// We log promise errors but do not otherwise track
|
|
243
|
+
// We log promise errors but do not otherwise track. This generally should not be invoked because types
|
|
244
|
+
// should align with this.#isAsync, so observers should not be returning promises
|
|
231
245
|
this.#handlePromise = (promise, observer) => {
|
|
232
246
|
promise.catch(error => {
|
|
233
247
|
let identity: string;
|
|
@@ -290,55 +304,103 @@ export class BasicObservable<T extends any[] = any[], R = void> implements Obser
|
|
|
290
304
|
}
|
|
291
305
|
|
|
292
306
|
// Iterate over a clone of observers so we do not trigger new observers added during observation
|
|
293
|
-
const
|
|
307
|
+
const observers = [...this.#observers];
|
|
308
|
+
|
|
309
|
+
let nextObserver = 0;
|
|
310
|
+
|
|
311
|
+
const next = () => {
|
|
312
|
+
const observer = observers[nextObserver];
|
|
313
|
+
|
|
314
|
+
if (this.#once?.has(observer)) {
|
|
315
|
+
this.#once.delete(observer);
|
|
316
|
+
this.#observers?.delete(observer);
|
|
317
|
+
}
|
|
294
318
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
319
|
+
if (this.#instrumentAs) {
|
|
320
|
+
logger.debug(
|
|
321
|
+
Diagnostic.strong(this.#instrumentAs),
|
|
322
|
+
`invoking #${nextObserver++}`,
|
|
323
|
+
Diagnostic.strong(observer.name || "(anon)"),
|
|
324
|
+
);
|
|
298
325
|
}
|
|
299
326
|
|
|
300
|
-
|
|
301
|
-
|
|
327
|
+
return observer;
|
|
328
|
+
};
|
|
302
329
|
|
|
303
|
-
|
|
330
|
+
let log: undefined | ((observer: Observer<any, any>) => void), done: undefined | (() => void);
|
|
331
|
+
if (this.#instrumentAs) {
|
|
332
|
+
logger.debug(Diagnostic.strong(this.#instrumentAs), "emitting");
|
|
304
333
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
334
|
+
log = (observer: Observer<any, any>) => {
|
|
335
|
+
logger.debug(
|
|
336
|
+
Diagnostic.strong(this.#instrumentAs),
|
|
337
|
+
`invoking #${nextObserver++}`,
|
|
338
|
+
Diagnostic.strong(observer.name || "(anon)"),
|
|
339
|
+
);
|
|
340
|
+
};
|
|
310
341
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
342
|
+
done = () => {
|
|
343
|
+
logger.debug(Diagnostic.strong(this.#instrumentAs), "emission complete");
|
|
344
|
+
};
|
|
345
|
+
}
|
|
315
346
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
347
|
+
// Initially emit using a synchronous loop. When we hit the first promies we convert to an async function
|
|
348
|
+
for (; nextObserver < observers.length; nextObserver++) {
|
|
349
|
+
let result: ReturnType<Observer<T, R>>;
|
|
350
|
+
|
|
351
|
+
let observer = next();
|
|
319
352
|
|
|
320
|
-
|
|
321
|
-
|
|
353
|
+
try {
|
|
354
|
+
log?.(observer);
|
|
355
|
+
result = observer(...payload);
|
|
356
|
+
} catch (e) {
|
|
357
|
+
this.#handleError(asError(e), observer);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (result === undefined) {
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
322
363
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
364
|
+
// If observer was async (which we can only conclude after invocation), switch to async emission
|
|
365
|
+
if (MaybePromise.is(result)) {
|
|
366
|
+
const emitAsync = async () => {
|
|
367
|
+
while (true) {
|
|
368
|
+
if (MaybePromise.is(result)) {
|
|
369
|
+
try {
|
|
370
|
+
result = await result;
|
|
371
|
+
} catch (e) {
|
|
372
|
+
this.#handleError(asError(e), observer);
|
|
327
373
|
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (result !== undefined) {
|
|
328
377
|
return result;
|
|
329
|
-
}
|
|
330
|
-
}
|
|
378
|
+
}
|
|
331
379
|
|
|
332
|
-
|
|
333
|
-
|
|
380
|
+
nextObserver++;
|
|
381
|
+
if (nextObserver >= observers.length) {
|
|
382
|
+
break;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
observer = next();
|
|
386
|
+
|
|
387
|
+
try {
|
|
388
|
+
result = observer(...payload);
|
|
389
|
+
} catch (e) {
|
|
390
|
+
this.#handleError(asError(e), observer);
|
|
391
|
+
}
|
|
334
392
|
}
|
|
335
|
-
}
|
|
336
393
|
|
|
337
|
-
|
|
394
|
+
done?.();
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
return emitAsync() as R | undefined;
|
|
338
398
|
}
|
|
339
|
-
};
|
|
340
399
|
|
|
341
|
-
|
|
400
|
+
done?.();
|
|
401
|
+
|
|
402
|
+
return result;
|
|
403
|
+
}
|
|
342
404
|
}
|
|
343
405
|
|
|
344
406
|
on(observer: Observer<T, R>) {
|
|
@@ -592,7 +654,7 @@ function constructAsyncObservableValue(value?: unknown, handleError?: ObserverEr
|
|
|
592
654
|
*/
|
|
593
655
|
export class EventEmitter {
|
|
594
656
|
// True private screws up TS types
|
|
595
|
-
private events?: Record<string,
|
|
657
|
+
private events?: Record<string, AsyncObservable | undefined>;
|
|
596
658
|
|
|
597
659
|
emit<This, N extends EventEmitter.NamesOf<This>>(this: This, name: N, ...payload: EventEmitter.PayloadOf<This, N>) {
|
|
598
660
|
event(this, name).emit(...payload);
|
|
@@ -614,7 +676,7 @@ export class EventEmitter {
|
|
|
614
676
|
event(this, name).off(handler as any);
|
|
615
677
|
}
|
|
616
678
|
|
|
617
|
-
addEvent(name: string, event?:
|
|
679
|
+
addEvent(name: string, event?: AsyncObservable) {
|
|
618
680
|
if (!this.events) {
|
|
619
681
|
this.events = {};
|
|
620
682
|
}
|
|
@@ -735,6 +797,18 @@ export class ObserverGroup {
|
|
|
735
797
|
this.#defaultTarget = target;
|
|
736
798
|
}
|
|
737
799
|
|
|
800
|
+
on<T extends any[], R>(
|
|
801
|
+
observable: Observable<T, R>,
|
|
802
|
+
observer: Observer<ObserverGroup.VarArgs<NoInfer<T>>, NoInfer<R>>,
|
|
803
|
+
target?: {},
|
|
804
|
+
): void;
|
|
805
|
+
|
|
806
|
+
on<T extends any[], R>(
|
|
807
|
+
observable: AsyncObservable<T, R>,
|
|
808
|
+
observer: AsyncObserver<ObserverGroup.VarArgs<NoInfer<T>>, NoInfer<R>>,
|
|
809
|
+
target?: {},
|
|
810
|
+
): void;
|
|
811
|
+
|
|
738
812
|
/**
|
|
739
813
|
* Add an observer.
|
|
740
814
|
*
|
|
@@ -820,20 +894,23 @@ export namespace ObserverGroup {
|
|
|
820
894
|
/**
|
|
821
895
|
* An {@link Observable} that emits an algorithmically-reduced number of events.
|
|
822
896
|
*/
|
|
823
|
-
export class QuietObservable<T extends any[] = any[]
|
|
897
|
+
export class QuietObservable<T extends any[] = any[], R extends MaybePromise<void> = void>
|
|
898
|
+
extends BasicObservable<T, R>
|
|
899
|
+
implements QuietObservable.State<T, R>
|
|
900
|
+
{
|
|
824
901
|
#emitAutomatically = QuietObservable.defaults.emitAutomatically;
|
|
825
902
|
#suppressionEnabled = QuietObservable.defaults.suppressionEnabled;
|
|
826
903
|
#minimumEmitInterval = QuietObservable.defaults.minimumEmitInterval;
|
|
827
904
|
#shouldEmit?: QuietObservable.EmitPredicate<T>;
|
|
828
|
-
#source?: Observable<T>;
|
|
829
|
-
#sink?: Observable<T>;
|
|
830
|
-
#sourceObserver?: Observer<T>;
|
|
831
|
-
#sinkObserver?: Observer<T>;
|
|
905
|
+
#source?: Observable<T, R>;
|
|
906
|
+
#sink?: Observable<T, R>;
|
|
907
|
+
#sourceObserver?: Observer<T, R>;
|
|
908
|
+
#sinkObserver?: Observer<T, R>;
|
|
832
909
|
#deferredPayload?: T;
|
|
833
910
|
#lastEmitAt?: number;
|
|
834
911
|
#emitTimer?: Timer;
|
|
835
912
|
|
|
836
|
-
constructor(config?: QuietObservable.Configuration<T>) {
|
|
913
|
+
constructor(config?: QuietObservable.Configuration<T, R>) {
|
|
837
914
|
super();
|
|
838
915
|
if (config) {
|
|
839
916
|
this.config = config;
|
|
@@ -844,7 +921,7 @@ export class QuietObservable<T extends any[] = any[]> extends BasicObservable<T>
|
|
|
844
921
|
return this;
|
|
845
922
|
}
|
|
846
923
|
|
|
847
|
-
set config(config: QuietObservable.Configuration<T>) {
|
|
924
|
+
set config(config: QuietObservable.Configuration<T, R>) {
|
|
848
925
|
const { suppressionEnabled, minimumEmitInterval, emitAutomatically } = config;
|
|
849
926
|
if (emitAutomatically !== undefined) {
|
|
850
927
|
this.emitAutomatically = emitAutomatically;
|
|
@@ -917,7 +994,7 @@ export class QuietObservable<T extends any[] = any[]> extends BasicObservable<T>
|
|
|
917
994
|
return this.#source;
|
|
918
995
|
}
|
|
919
996
|
|
|
920
|
-
set source(source: Observable<T> | undefined) {
|
|
997
|
+
set source(source: Observable<T, R> | undefined) {
|
|
921
998
|
if (this.#source === source) {
|
|
922
999
|
return;
|
|
923
1000
|
}
|
|
@@ -936,7 +1013,7 @@ export class QuietObservable<T extends any[] = any[]> extends BasicObservable<T>
|
|
|
936
1013
|
return this.#sink;
|
|
937
1014
|
}
|
|
938
1015
|
|
|
939
|
-
set sink(sink: Observable<T> | undefined) {
|
|
1016
|
+
set sink(sink: Observable<T, R> | undefined) {
|
|
940
1017
|
if (this.#sink === sink) {
|
|
941
1018
|
return;
|
|
942
1019
|
}
|
|
@@ -967,11 +1044,11 @@ export class QuietObservable<T extends any[] = any[]> extends BasicObservable<T>
|
|
|
967
1044
|
return super.isObserved || this.#sink?.isObserved || false;
|
|
968
1045
|
}
|
|
969
1046
|
|
|
970
|
-
override isObservedBy(observer: Observer<T>): boolean {
|
|
1047
|
+
override isObservedBy(observer: Observer<T, R>): boolean {
|
|
971
1048
|
return this.#sink?.isObservedBy(observer) || this.isObservedBy(observer) || false;
|
|
972
1049
|
}
|
|
973
1050
|
|
|
974
|
-
override emit(...payload: T) {
|
|
1051
|
+
override emit(...payload: T): R | undefined {
|
|
975
1052
|
const shouldEmit = this.#shouldEmit?.(...payload);
|
|
976
1053
|
if (shouldEmit === false) {
|
|
977
1054
|
return;
|
|
@@ -1022,7 +1099,7 @@ export class QuietObservable<T extends any[] = any[]> extends BasicObservable<T>
|
|
|
1022
1099
|
this.#deferredPayload = undefined;
|
|
1023
1100
|
this.#lastEmitAt = now ?? Time.nowMs;
|
|
1024
1101
|
this.#stop();
|
|
1025
|
-
super.emit(...payload);
|
|
1102
|
+
return super.emit(...payload);
|
|
1026
1103
|
}
|
|
1027
1104
|
|
|
1028
1105
|
#start(now?: number) {
|
|
@@ -1054,7 +1131,7 @@ export class QuietObservable<T extends any[] = any[]> extends BasicObservable<T>
|
|
|
1054
1131
|
}
|
|
1055
1132
|
|
|
1056
1133
|
export namespace QuietObservable {
|
|
1057
|
-
export interface State<T extends any[] = any[]> {
|
|
1134
|
+
export interface State<T extends any[] = any[], R extends MaybePromise<void> = void> {
|
|
1058
1135
|
/**
|
|
1059
1136
|
* If true this observable will emit within the suppression constraints. If false it will only emit after calls
|
|
1060
1137
|
* to {@link emitSoon} or {@link emitNow}.
|
|
@@ -1074,12 +1151,12 @@ export namespace QuietObservable {
|
|
|
1074
1151
|
/**
|
|
1075
1152
|
* An input observable this observable will automatically observe to produce events.
|
|
1076
1153
|
*/
|
|
1077
|
-
source?: Observable<T>;
|
|
1154
|
+
source?: Observable<T, R>;
|
|
1078
1155
|
|
|
1079
1156
|
/**
|
|
1080
1157
|
* An output observable this observable will automatically emit to whenever it emits.
|
|
1081
1158
|
*/
|
|
1082
|
-
sink?: Observable<T>;
|
|
1159
|
+
sink?: Observable<T, R>;
|
|
1083
1160
|
|
|
1084
1161
|
/**
|
|
1085
1162
|
* A predicate that determine whether a payload should emit.
|
|
@@ -1120,7 +1197,9 @@ export namespace QuietObservable {
|
|
|
1120
1197
|
(...payload: T): EmitDirective;
|
|
1121
1198
|
}
|
|
1122
1199
|
|
|
1123
|
-
export interface Configuration<T extends any[] = any[]> extends Partial<
|
|
1200
|
+
export interface Configuration<T extends any[] = any[], R extends MaybePromise<void> = void> extends Partial<
|
|
1201
|
+
State<T, R>
|
|
1202
|
+
> {}
|
|
1124
1203
|
|
|
1125
1204
|
export const defaults: State = {
|
|
1126
1205
|
emitAutomatically: true,
|
package/src/util/String.ts
CHANGED
|
@@ -8,177 +8,6 @@ import { ImplementationError } from "#MatterError.js";
|
|
|
8
8
|
import { Bytes } from "./Bytes.js";
|
|
9
9
|
import { isObject } from "./Type.js";
|
|
10
10
|
|
|
11
|
-
export function capitalize<T extends string>(text: T) {
|
|
12
|
-
return (text[0].toUpperCase() + text.slice(1)) as Capitalize<T>;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Converts identifiers of the form "foo-bar", "foo_bar", "foo bar", "foo*bar",
|
|
17
|
-
* "fooBar" or "FOOBar" into "fooBar" or "FooBar".
|
|
18
|
-
*/
|
|
19
|
-
export function camelize(name: string, upperFirst = false) {
|
|
20
|
-
const pieces = new Array<string>();
|
|
21
|
-
let pieceStart = 0,
|
|
22
|
-
sawUpper = false,
|
|
23
|
-
sawLower = false,
|
|
24
|
-
i = 0;
|
|
25
|
-
|
|
26
|
-
function addPiece(to: number) {
|
|
27
|
-
if (pieceStart < to) pieces.push(name.slice(pieceStart, to));
|
|
28
|
-
sawLower = sawUpper = false;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
for (; i < name.length; i++) {
|
|
32
|
-
if (name[i] >= "A" && name[i] <= "Z") {
|
|
33
|
-
if (sawLower) {
|
|
34
|
-
addPiece(i);
|
|
35
|
-
pieceStart = i;
|
|
36
|
-
}
|
|
37
|
-
sawUpper = true;
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (name[i] >= "a" && name[i] <= "z") {
|
|
42
|
-
if (!sawLower) {
|
|
43
|
-
if (sawUpper) {
|
|
44
|
-
addPiece(i - 1);
|
|
45
|
-
pieceStart = i - 1;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
sawLower = true;
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
addPiece(i);
|
|
53
|
-
|
|
54
|
-
if ((name[i] >= "0" && name[i] <= "9") || name[i] === "$") {
|
|
55
|
-
pieces.push(name[i]);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
pieceStart = i + 1;
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
addPiece(i);
|
|
62
|
-
|
|
63
|
-
let didFirst = false;
|
|
64
|
-
let result = pieces
|
|
65
|
-
.map(piece => {
|
|
66
|
-
let firstChar = piece[0];
|
|
67
|
-
if (upperFirst || didFirst) {
|
|
68
|
-
firstChar = firstChar.toUpperCase();
|
|
69
|
-
} else {
|
|
70
|
-
firstChar = firstChar.toLowerCase();
|
|
71
|
-
didFirst = true;
|
|
72
|
-
}
|
|
73
|
-
return `${firstChar}${piece.slice(1).toLowerCase()}`;
|
|
74
|
-
})
|
|
75
|
-
.join("");
|
|
76
|
-
|
|
77
|
-
// Special case so "100ths" doesn't become "100Ths" which is formally correct but goofy
|
|
78
|
-
result = result.replace(/(\d)Ths/i, "$1ths");
|
|
79
|
-
|
|
80
|
-
return result;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Converts an identifier from CamelCase to snake_case.
|
|
85
|
-
*/
|
|
86
|
-
export function decamelize(name: string, separator = "-") {
|
|
87
|
-
const result = Array<string>();
|
|
88
|
-
let needSeparator = false;
|
|
89
|
-
for (const c of name) {
|
|
90
|
-
if (c >= "A" && c <= "Z") {
|
|
91
|
-
if (needSeparator) {
|
|
92
|
-
result.push(separator);
|
|
93
|
-
needSeparator = false;
|
|
94
|
-
}
|
|
95
|
-
result.push(c.toLowerCase());
|
|
96
|
-
} else {
|
|
97
|
-
result.push(c);
|
|
98
|
-
needSeparator = true;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return result.join("");
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Like JSON.stringify but targets well-formed JS and is slightly more readable.
|
|
106
|
-
*/
|
|
107
|
-
export function serialize(value: unknown) {
|
|
108
|
-
const visited = new Set();
|
|
109
|
-
|
|
110
|
-
function asValidKey(key: string) {
|
|
111
|
-
if (key.match(/[a-z_$][\w$]*/i)) {
|
|
112
|
-
return key;
|
|
113
|
-
}
|
|
114
|
-
return JSON.stringify(key);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function serializeOne(value: unknown): string | undefined {
|
|
118
|
-
if (value === undefined) {
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
if (value === null) {
|
|
122
|
-
return "null";
|
|
123
|
-
}
|
|
124
|
-
if ((value as { [serialize.SERIALIZE](): string })[serialize.SERIALIZE]) {
|
|
125
|
-
return (value as { [serialize.SERIALIZE](): string })[serialize.SERIALIZE]();
|
|
126
|
-
}
|
|
127
|
-
if (typeof value === "function") {
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
if (typeof value === "bigint" || value instanceof BigInt) {
|
|
131
|
-
return value.toString();
|
|
132
|
-
}
|
|
133
|
-
if (typeof value === "number" || value instanceof Number) {
|
|
134
|
-
return value.toString();
|
|
135
|
-
}
|
|
136
|
-
if (typeof value === "string" || value instanceof String) {
|
|
137
|
-
return JSON.stringify(value);
|
|
138
|
-
}
|
|
139
|
-
if (typeof value === "boolean") {
|
|
140
|
-
return value ? "true" : "false";
|
|
141
|
-
}
|
|
142
|
-
if (Bytes.isBytes(value)) {
|
|
143
|
-
return Bytes.toHex(value);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Composite objects after this
|
|
147
|
-
if (visited.has(value)) {
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
if ((value as { toJSON(): string }).toJSON) {
|
|
151
|
-
value = JSON.parse(JSON.stringify(value));
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
try {
|
|
155
|
-
visited.add(value);
|
|
156
|
-
|
|
157
|
-
if (Array.isArray(value)) {
|
|
158
|
-
if (value.length) {
|
|
159
|
-
return `[ ${value.map(serializeOne).join(", ")} ]`;
|
|
160
|
-
}
|
|
161
|
-
return "[]";
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const entries = Object.entries(value as {})
|
|
165
|
-
.map(([k, v]) => [k, serializeOne(v)])
|
|
166
|
-
.filter(([_k, v]) => v !== undefined)
|
|
167
|
-
.map(([k, v]) => `${asValidKey(k ?? "")}: ${v}`);
|
|
168
|
-
|
|
169
|
-
if (!entries.length) {
|
|
170
|
-
return "{}";
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return `{ ${entries.join(", ")} }`;
|
|
174
|
-
} finally {
|
|
175
|
-
visited.delete(value);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
return serializeOne(value);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
11
|
/**
|
|
183
12
|
* Like {@link JSON.stringify} but with support for additional standard objects.
|
|
184
13
|
*/
|
|
@@ -198,50 +27,6 @@ export function asJson(value: unknown, space?: number) {
|
|
|
198
27
|
);
|
|
199
28
|
}
|
|
200
29
|
|
|
201
|
-
export namespace serialize {
|
|
202
|
-
/**
|
|
203
|
-
* Custom serialization function key.
|
|
204
|
-
*/
|
|
205
|
-
export const SERIALIZE = Symbol("SERIALIZE");
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Mark a value as serialized so the serializer just uses its string
|
|
209
|
-
* representation.
|
|
210
|
-
*/
|
|
211
|
-
export function asIs(value: any) {
|
|
212
|
-
if (typeof value === "string") {
|
|
213
|
-
value = new String(value);
|
|
214
|
-
}
|
|
215
|
-
if (value !== undefined && value !== null) {
|
|
216
|
-
value[SERIALIZE] = function () {
|
|
217
|
-
return this.toString();
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
return value;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Test whether a value serializes as a structure or a primitive.
|
|
225
|
-
*/
|
|
226
|
-
export function isPrimitive(value: any) {
|
|
227
|
-
if (
|
|
228
|
-
value === undefined ||
|
|
229
|
-
value === null ||
|
|
230
|
-
value instanceof Date ||
|
|
231
|
-
ArrayBuffer.isView(value) ||
|
|
232
|
-
value[SERIALIZE]
|
|
233
|
-
) {
|
|
234
|
-
return true;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
if (Array.isArray(value)) {
|
|
238
|
-
return false;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
return typeof value !== "object";
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
30
|
/**
|
|
246
31
|
* Create a human readable version of a list of items.
|
|
247
32
|
*/
|
|
@@ -326,3 +111,17 @@ export function serializeToJs(value: unknown) {
|
|
|
326
111
|
parts.push("}");
|
|
327
112
|
return parts.join("");
|
|
328
113
|
}
|
|
114
|
+
|
|
115
|
+
export namespace hex {
|
|
116
|
+
export function fixed(value: number | bigint, width: number) {
|
|
117
|
+
return value.toString(16).padStart(width, "0");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function byte(value: number | bigint) {
|
|
121
|
+
return fixed(value, 2);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function word(value: number | bigint) {
|
|
125
|
+
return fixed(value, 4);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2022-2025 Matter.js Authors
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Make first character uppercase.
|
|
9
|
+
*/
|
|
10
|
+
export function capitalize<T extends string>(text: T) {
|
|
11
|
+
return (text[0].toUpperCase() + text.slice(1)) as Capitalize<T>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Converts identifiers of the form "foo-bar", "foo_bar", "foo bar", "foo*bar",
|
|
16
|
+
* "fooBar" or "FOOBar" into "fooBar" or "FooBar".
|
|
17
|
+
*/
|
|
18
|
+
export function camelize(name: string, upperFirst = false) {
|
|
19
|
+
const pieces = new Array<string>();
|
|
20
|
+
let pieceStart = 0,
|
|
21
|
+
sawUpper = false,
|
|
22
|
+
sawLower = false,
|
|
23
|
+
i = 0;
|
|
24
|
+
|
|
25
|
+
function addPiece(to: number) {
|
|
26
|
+
if (pieceStart < to) pieces.push(name.slice(pieceStart, to));
|
|
27
|
+
sawLower = sawUpper = false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
for (; i < name.length; i++) {
|
|
31
|
+
if (name[i] >= "A" && name[i] <= "Z") {
|
|
32
|
+
if (sawLower) {
|
|
33
|
+
addPiece(i);
|
|
34
|
+
pieceStart = i;
|
|
35
|
+
}
|
|
36
|
+
sawUpper = true;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (name[i] >= "a" && name[i] <= "z") {
|
|
41
|
+
if (!sawLower) {
|
|
42
|
+
if (sawUpper) {
|
|
43
|
+
addPiece(i - 1);
|
|
44
|
+
pieceStart = i - 1;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
sawLower = true;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
addPiece(i);
|
|
52
|
+
|
|
53
|
+
if ((name[i] >= "0" && name[i] <= "9") || name[i] === "$") {
|
|
54
|
+
pieces.push(name[i]);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
pieceStart = i + 1;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
addPiece(i);
|
|
61
|
+
|
|
62
|
+
let didFirst = false;
|
|
63
|
+
let result = pieces
|
|
64
|
+
.map(piece => {
|
|
65
|
+
let firstChar = piece[0];
|
|
66
|
+
if (upperFirst || didFirst) {
|
|
67
|
+
firstChar = firstChar.toUpperCase();
|
|
68
|
+
} else {
|
|
69
|
+
firstChar = firstChar.toLowerCase();
|
|
70
|
+
didFirst = true;
|
|
71
|
+
}
|
|
72
|
+
return `${firstChar}${piece.slice(1).toLowerCase()}`;
|
|
73
|
+
})
|
|
74
|
+
.join("");
|
|
75
|
+
|
|
76
|
+
// Special case so "100ths" doesn't become "100Ths" which is formally correct but goofy
|
|
77
|
+
result = result.replace(/(\d)Ths/i, "$1ths");
|
|
78
|
+
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Converts an identifier from CamelCase to snake_case.
|
|
84
|
+
*/
|
|
85
|
+
export function decamelize(name: string, separator = "-") {
|
|
86
|
+
const result = Array<string>();
|
|
87
|
+
let needSeparator = false;
|
|
88
|
+
for (const c of name) {
|
|
89
|
+
if (c >= "A" && c <= "Z") {
|
|
90
|
+
if (needSeparator) {
|
|
91
|
+
result.push(separator);
|
|
92
|
+
needSeparator = false;
|
|
93
|
+
}
|
|
94
|
+
result.push(c.toLowerCase());
|
|
95
|
+
} else {
|
|
96
|
+
result.push(c);
|
|
97
|
+
needSeparator = true;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return result.join("");
|
|
101
|
+
}
|