better-lock 1.0.0 → 2.0.1-beta

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.
Files changed (54) hide show
  1. package/.eslintrc.js +24 -0
  2. package/.idea/better-lock.iml +1 -7
  3. package/.idea/codeStyles/Project.xml +336 -5
  4. package/.idea/compiler.xml +7 -0
  5. package/.idea/inspectionProfiles/Project_Default.xml +6 -2
  6. package/.idea/jsLibraryMappings.xml +0 -1
  7. package/.idea/modules.xml +1 -1
  8. package/.idea/prettier.xml +8 -0
  9. package/.prettierrc.yaml +1 -3
  10. package/.travis.yml +2 -2
  11. package/README.md +102 -53
  12. package/dist/better_lock.d.ts +50 -0
  13. package/dist/better_lock.js +317 -0
  14. package/dist/better_lock.js.map +1 -0
  15. package/dist/errors.d.ts +44 -0
  16. package/dist/errors.js +131 -0
  17. package/dist/errors.js.map +1 -0
  18. package/dist/index.d.ts +16 -0
  19. package/dist/index.js +28 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/internals.d.ts +75 -0
  22. package/dist/internals.js +75 -0
  23. package/dist/internals.js.map +1 -0
  24. package/dist/options.d.ts +54 -0
  25. package/dist/options.js +3 -0
  26. package/dist/options.js.map +1 -0
  27. package/dist/tools.d.ts +19 -0
  28. package/dist/tools.js +109 -0
  29. package/dist/tools.js.map +1 -0
  30. package/dist/types.d.ts +23 -0
  31. package/dist/types.js +3 -0
  32. package/dist/types.js.map +1 -0
  33. package/jest.config.js +4 -0
  34. package/package.json +30 -8
  35. package/spec/better_lock.spec.ts +614 -0
  36. package/src/better_lock.ts +430 -0
  37. package/src/errors.ts +124 -0
  38. package/src/index.ts +54 -0
  39. package/src/internals.ts +118 -0
  40. package/src/options.ts +63 -0
  41. package/src/tools.ts +111 -0
  42. package/src/types.ts +29 -0
  43. package/tsconfig.json +13 -0
  44. package/.idea/markdown-navigator/profiles_settings.xml +0 -3
  45. package/.idea/markdown-navigator.xml +0 -78
  46. package/.idea/misc.xml +0 -6
  47. package/.idea/watcherTasks.xml +0 -8
  48. package/index.js +0 -19
  49. package/spec/better_lock.spec.js +0 -583
  50. package/src/better_lock.js +0 -400
  51. package/src/errors.js +0 -81
  52. package/src/options.js +0 -104
  53. package/src/tools.js +0 -125
  54. package/src/types.js +0 -110
@@ -0,0 +1,118 @@
1
+ import { ILockJobOptions } from './options';
2
+ import { ICallback, IExecutor } from './types';
3
+
4
+ /**
5
+ * Class that represents one scheduled job and its state. This is created every time user calls "acquire"
6
+ */
7
+ export class LockJob<TResult> {
8
+ static _lastId = 0;
9
+
10
+ /** ID to uniquely identify this particular job */
11
+ public id = undefined;
12
+
13
+ /**
14
+ * List of keys that will be locked. Always an array
15
+ */
16
+ public keys: Array<string | null>;
17
+
18
+ /**
19
+ * Number of keys which this job is waiting on. Once this reaches 0, job will be executed
20
+ */
21
+ public waiting_count: number;
22
+
23
+ /**
24
+ * Function that will be called inside the lock
25
+ */
26
+ public executor: IExecutor<TResult>;
27
+
28
+ /**
29
+ * Function that will be called once job is done, whether it succeeds or times out.
30
+ * This is supplied by user in callback API, and generated internally otherwise.
31
+ */
32
+ public callback: ICallback<TResult>;
33
+
34
+ /**
35
+ * Timestamp when job was created
36
+ */
37
+ public enqueued_at: Date = new Date();
38
+
39
+ /**
40
+ * Timestamp when job was executed
41
+ */
42
+ public executed_at: Date = null;
43
+
44
+ /**
45
+ * This is set when job ends, in case something holds a reference to it
46
+ */
47
+ public ended_at: Date = null;
48
+
49
+ /**
50
+ * Options that apply to this particular job
51
+ */
52
+ public options: ILockJobOptions;
53
+
54
+ /**
55
+ * Id of the wait timer
56
+ */
57
+ public wait_timeout_id = null;
58
+
59
+ /**
60
+ * Id of the execution timer
61
+ */
62
+ public execution_timeout_id = null;
63
+
64
+ /**
65
+ * Saved incoming stack, for the purpose of extending stack traces
66
+ */
67
+ public incoming_stack: string = null;
68
+
69
+ constructor(
70
+ keys: Array<string | null>,
71
+ executor: IExecutor<TResult>,
72
+ callback: ICallback<TResult>,
73
+ options: ILockJobOptions
74
+ ) {
75
+ LockJob._lastId++;
76
+ this.id = LockJob._lastId;
77
+ this.keys = keys;
78
+ this.waiting_count = keys.length;
79
+ this.executor = executor;
80
+ this.callback = callback;
81
+ this.options = options;
82
+ }
83
+
84
+ toString() {
85
+ return `Job #${this.id} [${this.keys.join(', ')}]`;
86
+ }
87
+ }
88
+
89
+ // *********************************************************************************************************************
90
+
91
+ /**
92
+ * Class to hold pending, holding and executing jobs for one key
93
+ */
94
+ export class KeyQueue {
95
+ // We will use this key when user doesn't supply anything
96
+ static readonly DEFAULT_QUEUE_KEY = '___DEFAULT_QUEUE_KEY___';
97
+
98
+ public key: string;
99
+
100
+ /**
101
+ * Queue of jobs waiting to be executed.
102
+ * Jobs in this state are governed by the wait_timeout.
103
+ */
104
+ public jobs: LockJob<any>[] = [];
105
+
106
+ /**
107
+ * Active job that is holding this key. This job could be waiting on other keys or executing.
108
+ */
109
+ public active: LockJob<any> = null;
110
+
111
+ constructor(key?: string) {
112
+ this.key = key;
113
+ }
114
+
115
+ toString() {
116
+ return this.key === KeyQueue.DEFAULT_QUEUE_KEY ? 'JobQueue' : `JobQueue<${this.key}>`;
117
+ }
118
+ }
package/src/options.ts ADDED
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Options that apply on LockJob
3
+ */
4
+ export interface ILockJobOptions {
5
+ /**
6
+ * How long can jobs wait in queue before timing out. Overrides BetterLockOptions value.
7
+ */
8
+ wait_timeout?: number | null;
9
+
10
+ /**
11
+ * How long can a job be executing before timing out. Null to disable timeout. Overrides BetterLockOptions value.
12
+ */
13
+ execution_timeout?: number | null;
14
+
15
+ /**
16
+ * In any error produced by BetterLock, extend the stack trace to include incoming trace. Overrides BetterLockOptions value.
17
+ */
18
+ extend_stack_traces?: boolean;
19
+ }
20
+
21
+ /**
22
+ * Options that apply on BetterLock instance
23
+ */
24
+ export interface IOptions extends ILockJobOptions {
25
+ /**
26
+ * How long can jobs wait in queue before timing out. Null to disable timeout.
27
+ */
28
+ wait_timeout?: number | null;
29
+
30
+ /**
31
+ * How long can a job be executing before timing out. Null to disable timeout.
32
+ * If you do that, though, and you have a swallowed callback, the lock can remain locked permanently.
33
+ */
34
+ execution_timeout?: number | null;
35
+
36
+ /**
37
+ * Max queue size for waiting jobs.
38
+ */
39
+ queue_size?: number | null;
40
+
41
+ /**
42
+ * Lock name. This will be written in all logs and error messages, to help you distinguish between different locks
43
+ */
44
+ name?: string;
45
+
46
+ /**
47
+ * Whether to log the internal actions. Set to true to use console.log. Alternatively, provide your own function
48
+ */
49
+ log?: boolean | ((msg: string) => void);
50
+
51
+ /**
52
+ * In any error produced by BetterLock, extend the stack trace to include incoming trace.
53
+ * Defaults to true.
54
+ */
55
+ extend_stack_traces?: boolean;
56
+
57
+ /**
58
+ * Function that detects whether value returned by executor is a promise.
59
+ * This will ducktype-sniff the returned value. Depending on your Promise library of choice,
60
+ * you might want to tighten or loosen this bit.
61
+ */
62
+ promise_tester?: (maybePromise: any) => boolean;
63
+ }
package/src/tools.ts ADDED
@@ -0,0 +1,111 @@
1
+ import { ICallback } from './types';
2
+
3
+ export function isString(val) {
4
+ return typeof val === 'string';
5
+ }
6
+
7
+ export function isFunction(val) {
8
+ return typeof val === 'function';
9
+ }
10
+
11
+ export function isObject(val) {
12
+ return typeof val === 'object' && val !== null && !Array.isArray(val);
13
+ }
14
+
15
+ export function isNumber(val, includeInfinity = false, includeNaN = false) {
16
+ if (typeof val !== 'number') {
17
+ return false;
18
+ }
19
+ if (!includeInfinity && (val === Number.POSITIVE_INFINITY || val === Number.NEGATIVE_INFINITY)) {
20
+ return false;
21
+ }
22
+ if (!includeNaN && isNaN(val)) {
23
+ return false;
24
+ }
25
+ return true;
26
+ }
27
+
28
+ export function noop() {
29
+ // Nothing
30
+ }
31
+
32
+ export function makeLog(name, doLog): (msg: string) => void {
33
+ if (!doLog) {
34
+ return noop;
35
+ }
36
+
37
+ if (doLog === true) {
38
+ doLog = console.log.bind(console);
39
+ }
40
+
41
+ if (name) {
42
+ name = '[' + name + '] ';
43
+ }
44
+
45
+ return function log(msg) {
46
+ if (name) {
47
+ doLog(name + msg);
48
+ } else {
49
+ doLog(msg);
50
+ }
51
+ };
52
+ }
53
+
54
+ /**
55
+ * Create a wrapped promise + callback construct
56
+ */
57
+ export function callbackWithPromise<TResult = any>(): Promise<TResult> & {
58
+ callback: ICallback<TResult>;
59
+ } {
60
+ let callback;
61
+ const promise = new Promise((resolve, reject) => {
62
+ callback = (err, result) => {
63
+ if (err) {
64
+ reject(err);
65
+ } else {
66
+ resolve(result);
67
+ }
68
+ };
69
+ });
70
+ callback.promise = promise;
71
+ return callback;
72
+ }
73
+
74
+ /**
75
+ * Assign properties from sources to target, skipping over undefined-s
76
+ * @param target Target object which will be mutated
77
+ * @param sources One or more source objects which will provide properties
78
+ */
79
+ export function assign<T>(target: T, sources: T[]): T {
80
+ for (const source of sources) {
81
+ if (!isObject(source)) {
82
+ // We must have an object to assign
83
+ continue;
84
+ }
85
+
86
+ for (const key in source) {
87
+ if (!source.hasOwnProperty(key)) {
88
+ continue;
89
+ }
90
+
91
+ const sourceValue = source[key];
92
+ if (sourceValue === undefined) {
93
+ // Do not overwrite undefineds
94
+ continue;
95
+ }
96
+
97
+ const targetValue = target[key];
98
+ if (isObject(sourceValue) && isObject(targetValue)) {
99
+ // Merge deep.
100
+ const cloneRoot = targetValue.constructor ? new (targetValue.constructor as any)() : {};
101
+ target[key] = assign(cloneRoot, [targetValue, sourceValue]);
102
+ continue;
103
+ }
104
+
105
+ // In all other cases, copy by reference
106
+ target[key] = sourceValue;
107
+ }
108
+ }
109
+
110
+ return target;
111
+ }
package/src/types.ts ADDED
@@ -0,0 +1,29 @@
1
+ import * as errors from './errors';
2
+
3
+ type IErrorExports = typeof errors;
4
+
5
+ /**
6
+ * All error names that we can emit
7
+ */
8
+ export type IErrorName = keyof IErrorExports;
9
+
10
+ /**
11
+ * User-supplied callback. We support a "canonical" callback, with just error and result,
12
+ * but also old-school callbacks, with varargs return values.
13
+ */
14
+ export type ICallback<TResult> = TResult extends any[]
15
+ ? (err?: Error, ...result: TResult) => void
16
+ : (err?: Error, result?: TResult) => void;
17
+
18
+ /**
19
+ * Function that user will supply as the thing that will be running inside lock.
20
+ * It can either accept a done() function, or return a promise.
21
+ */
22
+ export type ICallbackExecutor<TResult> = (lockDone: ICallback<TResult>) => void;
23
+ export type IPromiseExecutor<TResult> = () => Promise<TResult>;
24
+ export type IExecutor<TResult> = ICallbackExecutor<TResult> | IPromiseExecutor<TResult>;
25
+
26
+ /**
27
+ * Key types that can be used for a lock.
28
+ */
29
+ export type ILockKey = string | number | null | undefined;
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es5",
4
+ "module": "commonjs",
5
+ "declaration": true,
6
+ "outDir": "./dist",
7
+ "strict": false,
8
+ "noImplicitAny": false,
9
+ "sourceMap": true
10
+ },
11
+ "include": ["src"],
12
+ "exclude": ["node_modules", "**/__tests__/*"]
13
+ }
@@ -1,3 +0,0 @@
1
- <component name="MarkdownNavigator.ProfileManager">
2
- <settings default="" pdf-export="" />
3
- </component>
@@ -1,78 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="MarkdownProjectSettings" wasCopied="false">
4
- <PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.25" maxImageWidth="0" showGitHubPageIfSynced="false" allowBrowsingInPreview="false" synchronizePreviewPosition="true" highlightPreviewType="NONE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true" verticallyAlignSourceAndPreviewSyncPosition="true" showSearchHighlightsInPreview="false" showSelectionInPreview="true" openRemoteLinks="true" replaceUnicodeEmoji="false" lastLayoutSetsDefault="false">
5
- <PanelProvider>
6
- <provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.panel" providerName="Default - Swing" />
7
- </PanelProvider>
8
- </PreviewSettings>
9
- <ParserSettings gitHubSyntaxChange="false" emojiShortcuts="1" emojiImages="0">
10
- <PegdownExtensions>
11
- <option name="ABBREVIATIONS" value="false" />
12
- <option name="ANCHORLINKS" value="true" />
13
- <option name="ASIDE" value="false" />
14
- <option name="ATXHEADERSPACE" value="true" />
15
- <option name="AUTOLINKS" value="true" />
16
- <option name="DEFINITIONS" value="false" />
17
- <option name="DEFINITION_BREAK_DOUBLE_BLANK_LINE" value="false" />
18
- <option name="FENCED_CODE_BLOCKS" value="true" />
19
- <option name="FOOTNOTES" value="false" />
20
- <option name="HARDWRAPS" value="false" />
21
- <option name="HTML_DEEP_PARSER" value="false" />
22
- <option name="INSERTED" value="false" />
23
- <option name="QUOTES" value="false" />
24
- <option name="RELAXEDHRULES" value="true" />
25
- <option name="SMARTS" value="false" />
26
- <option name="STRIKETHROUGH" value="true" />
27
- <option name="SUBSCRIPT" value="false" />
28
- <option name="SUPERSCRIPT" value="false" />
29
- <option name="SUPPRESS_HTML_BLOCKS" value="false" />
30
- <option name="SUPPRESS_INLINE_HTML" value="false" />
31
- <option name="TABLES" value="true" />
32
- <option name="TASKLISTITEMS" value="true" />
33
- <option name="TOC" value="false" />
34
- <option name="WIKILINKS" value="true" />
35
- </PegdownExtensions>
36
- <ParserOptions>
37
- <option name="ADMONITION_EXT" value="false" />
38
- <option name="ATTRIBUTES_EXT" value="false" />
39
- <option name="COMMONMARK_LISTS" value="true" />
40
- <option name="DUMMY" value="false" />
41
- <option name="EMOJI_SHORTCUTS" value="true" />
42
- <option name="ENUMERATED_REFERENCES_EXT" value="false" />
43
- <option name="FLEXMARK_FRONT_MATTER" value="false" />
44
- <option name="GFM_LOOSE_BLANK_LINE_AFTER_ITEM_PARA" value="false" />
45
- <option name="GFM_TABLE_RENDERING" value="true" />
46
- <option name="GITBOOK_URL_ENCODING" value="false" />
47
- <option name="GITHUB_LISTS" value="false" />
48
- <option name="GITHUB_WIKI_LINKS" value="true" />
49
- <option name="HEADER_ID_NO_DUPED_DASHES" value="false" />
50
- <option name="JEKYLL_FRONT_MATTER" value="false" />
51
- <option name="NO_TEXT_ATTRIBUTES" value="false" />
52
- <option name="PARSE_HTML_ANCHOR_ID" value="false" />
53
- <option name="SIM_TOC_BLANK_LINE_SPACER" value="true" />
54
- </ParserOptions>
55
- </ParserSettings>
56
- <HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" embedUrlContent="false" addPageHeader="true" embedImages="false" embedHttpImages="false" imageUriSerials="false">
57
- <GeneratorProvider>
58
- <provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.generator" providerName="Default Swing HTML Generator" />
59
- </GeneratorProvider>
60
- <headerTop />
61
- <headerBottom />
62
- <bodyTop />
63
- <bodyBottom />
64
- </HtmlSettings>
65
- <CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssUriSerial="true" isCssTextEnabled="false" isDynamicPageWidth="true">
66
- <StylesheetProvider>
67
- <provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.css" providerName="Default Swing Stylesheet" />
68
- </StylesheetProvider>
69
- <ScriptProviders />
70
- <cssText />
71
- <cssUriHistory />
72
- </CssSettings>
73
- <HtmlExportSettings updateOnSave="false" parentDir="" targetDir="" cssDir="" scriptDir="" plainHtml="false" imageDir="" copyLinkedImages="false" imageUniquifyType="0" targetExt="" useTargetExt="false" noCssNoScripts="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" linkFormatType="HTTP_ABSOLUTE" />
74
- <LinkMapSettings>
75
- <textMaps />
76
- </LinkMapSettings>
77
- </component>
78
- </project>
package/.idea/misc.xml DELETED
@@ -1,6 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="JavaScriptSettings">
4
- <option name="languageLevel" value="ES6" />
5
- </component>
6
- </project>
@@ -1,8 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="ProjectTasksOptions" suppressed-tasks="Babel">
4
- <enabled-global>
5
- <option value="Prettier on save (js)" />
6
- </enabled-global>
7
- </component>
8
- </project>
package/index.js DELETED
@@ -1,19 +0,0 @@
1
- const { BetterLock } = require('./src/better_lock');
2
- const errors = require('./src/errors');
3
- const { LockJob } = require('./src/types');
4
- const { BetterLockOptions } = require('./src/options');
5
-
6
- BetterLock.BetterLock = BetterLock;
7
-
8
- BetterLock.BetterLockError = errors.BetterLockError;
9
- BetterLock.BetterLockInternalError = errors.BetterLockInternalError;
10
- BetterLock.InvalidArgumentError = errors.InvalidArgumentError;
11
- BetterLock.WaitTimeoutError = errors.WaitTimeoutError;
12
- BetterLock.ExecutionTimeoutError = errors.ExecutionTimeoutError;
13
- BetterLock.QueueOverflowError = errors.QueueOverflowError;
14
- BetterLock.JobAbortedError = errors.JobAbortedError;
15
-
16
- BetterLock.LockJob = LockJob;
17
- BetterLock.Options = BetterLockOptions;
18
-
19
- module.exports = BetterLock;