@webex/common-timers 3.12.0-next.1 → 3.12.0-next.10
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/.repo-context.json +534 -0
- package/.sdd/manifest.json +268 -0
- package/AGENTS.md +240 -0
- package/docs/adr/index.md +34 -0
- package/docs/architecture.md +243 -0
- package/docs/getting-started.md +121 -0
- package/docs/index.md +50 -0
- package/docs/specs/README.md +86 -0
- package/package.json +5 -5
- package/schemas/repo-context.schema.json +796 -0
- package/src/docs/README.md +460 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
{
|
|
2
|
+
"manifest_version": 1,
|
|
3
|
+
"repository": {
|
|
4
|
+
"name": "@webex/common-timers",
|
|
5
|
+
"purpose": "Timer primitives for the Cisco Webex JS SDK that never hold a Node process open: unref-ing wrappers over setTimeout and setInterval, plus a restartable one-shot Timer with a guarded init-running-done lifecycle.",
|
|
6
|
+
"category": "cat2-legacy",
|
|
7
|
+
"primary_languages": [
|
|
8
|
+
"typescript"
|
|
9
|
+
]
|
|
10
|
+
},
|
|
11
|
+
"topology": "Single-repo",
|
|
12
|
+
"commands": {
|
|
13
|
+
"install": {
|
|
14
|
+
"command": "yarn install",
|
|
15
|
+
"source_file": "package.json",
|
|
16
|
+
"role": "install"
|
|
17
|
+
},
|
|
18
|
+
"compile": {
|
|
19
|
+
"command": "yarn workspace @webex/common-timers build:src",
|
|
20
|
+
"source_file": "package.json",
|
|
21
|
+
"role": "compile"
|
|
22
|
+
},
|
|
23
|
+
"build": {
|
|
24
|
+
"command": "yarn workspace @webex/common-timers build",
|
|
25
|
+
"source_file": "package.json",
|
|
26
|
+
"role": "build"
|
|
27
|
+
},
|
|
28
|
+
"unit-test": {
|
|
29
|
+
"command": "yarn workspace @webex/common-timers test:unit",
|
|
30
|
+
"source_file": "package.json",
|
|
31
|
+
"role": "unit-test"
|
|
32
|
+
},
|
|
33
|
+
"lint": {
|
|
34
|
+
"command": "yarn workspace @webex/common-timers test:style",
|
|
35
|
+
"source_file": "package.json",
|
|
36
|
+
"role": "lint"
|
|
37
|
+
},
|
|
38
|
+
"deploy": {
|
|
39
|
+
"command": "yarn workspace @webex/common-timers deploy:npm",
|
|
40
|
+
"source_file": "package.json",
|
|
41
|
+
"role": "deploy"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"toolchain": [
|
|
45
|
+
{
|
|
46
|
+
"name": "node",
|
|
47
|
+
"version": ">=18",
|
|
48
|
+
"origin": "repo-config",
|
|
49
|
+
"source_file": "package.json"
|
|
50
|
+
}
|
|
51
|
+
],
|
|
52
|
+
"tests": {
|
|
53
|
+
"unit": {
|
|
54
|
+
"command_ref": "unit-test",
|
|
55
|
+
"dir": "test/unit",
|
|
56
|
+
"framework": "jest"
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"quality_gates": {
|
|
60
|
+
"code_coverage": {
|
|
61
|
+
"origin": "none",
|
|
62
|
+
"declared_by": "riag@cisco.com via bootstrap questionnaire"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"coverage_status_definitions": {
|
|
66
|
+
"specced": ">=80% public surface specced, drift <5% - spec is authoritative",
|
|
67
|
+
"partial": "40-80% specced - spec is a hint, cross-check code",
|
|
68
|
+
"untracked": "<40% specced - code is the source of truth"
|
|
69
|
+
},
|
|
70
|
+
"modules": [
|
|
71
|
+
{
|
|
72
|
+
"path": "src",
|
|
73
|
+
"coverage_status": "Partial",
|
|
74
|
+
"coverage_evidence": "Field coverage 100% (14/14) assessed 2026-09-24; critical field coverage 9/9. Test coverage is incomplete: the unit suite is 13 tests in 1 file (test/unit/spec/index.ts), all passing as of 2026-10-05. That suite is pre-existing and was not authored by this specification change. MOD-002, MOD-004, MOD-009 and MOD-012 have no automated evidence and are scored Weak; MOD-001 and MOD-003 are partly covered. Nothing asserts that unref() is called, so the package's core guarantee rests on code reading rather than on a test. This module has no characterization baseline, so the Timer constructor's missing duration validation is specified but unpinned; it was triaged on 2026-09-24 as fix-separately and the caller-facing hazard is recorded in the module spec under Pitfalls and constraints. Independent validation by codex ran four times across 2026-09-24 and 2026-09-28, but that assessment covered a test suite that has since been withdrawn from this change, so it is stale and revalidation is required. Promotion to Specced is held pending unref and re-entrancy assertions, a characterization baseline, and a clean validator run.",
|
|
75
|
+
"canonical_spec": "src/docs/README.md",
|
|
76
|
+
"contracts": {
|
|
77
|
+
"provides": [
|
|
78
|
+
"common-timers-sdk"
|
|
79
|
+
],
|
|
80
|
+
"requires": [
|
|
81
|
+
"host-timer-api"
|
|
82
|
+
]
|
|
83
|
+
},
|
|
84
|
+
"last_assessed": "2026-09-24",
|
|
85
|
+
"section_profile": {
|
|
86
|
+
"has_ui": false,
|
|
87
|
+
"crosses_service_boundaries": false,
|
|
88
|
+
"enforces_domain_rules": false,
|
|
89
|
+
"is_concurrent_async": true,
|
|
90
|
+
"owns_persistence": false,
|
|
91
|
+
"returns_caller_errors": true,
|
|
92
|
+
"has_design_tradeoff": true,
|
|
93
|
+
"stateful_transitions": true,
|
|
94
|
+
"exposes_wire_protocol": false,
|
|
95
|
+
"ui_multi_screen": false,
|
|
96
|
+
"large_data_model": false,
|
|
97
|
+
"has_tiers": false,
|
|
98
|
+
"module_specific_conventions": false,
|
|
99
|
+
"published_package": true,
|
|
100
|
+
"embedded_in_host": false,
|
|
101
|
+
"holds_client_state": false,
|
|
102
|
+
"has_submodules": false,
|
|
103
|
+
"has_submodules_computed_by": "scripts/module_tree.py",
|
|
104
|
+
"has_submodules_computed_at": "2026-09-24T04:51:24Z",
|
|
105
|
+
"resolved_by": "claude-code/claude-opus-5 (host metadata; bootstrap questionnaire)",
|
|
106
|
+
"resolved_at": "2026-09-24T04:51:24Z"
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
],
|
|
110
|
+
"contract_catalog": {
|
|
111
|
+
"index_path": "docs/architecture.md",
|
|
112
|
+
"definitions": {
|
|
113
|
+
"common-timers-sdk": {
|
|
114
|
+
"kind": "sdk",
|
|
115
|
+
"publication": "published",
|
|
116
|
+
"source_kind": "repository-path",
|
|
117
|
+
"source": "package.json",
|
|
118
|
+
"summary": "Published npm package export surface declared by main and devMain: safeSetTimeout and safeSetInterval, which return the platform timer handle after unref-ing it where that is supported, and the Timer class, a restartable one-shot timer whose start, reset, and cancel methods throw on an invalid lifecycle transition. Per-export detail lives in the owning module spec Public surface."
|
|
119
|
+
},
|
|
120
|
+
"host-timer-api": {
|
|
121
|
+
"kind": "sdk",
|
|
122
|
+
"publication": "published",
|
|
123
|
+
"source_kind": "external",
|
|
124
|
+
"source": "Host JavaScript runtime timer API: setTimeout, setInterval, and clearTimeout, plus the Node.js Timeout.unref() extension",
|
|
125
|
+
"summary": "The platform scheduling primitives the module wraps. Node returns a Timeout object carrying unref(); browsers return a numeric handle without it, which is why every wrapper feature-detects unref rather than assuming a platform."
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
"spec_policy": {
|
|
130
|
+
"require_what_and_why": true,
|
|
131
|
+
"require_provenance": true,
|
|
132
|
+
"protected_specs": [
|
|
133
|
+
"src/docs/README.md"
|
|
134
|
+
]
|
|
135
|
+
},
|
|
136
|
+
"validation": {
|
|
137
|
+
"generator_runtime": "claude-code",
|
|
138
|
+
"generator_model": "claude-opus-5",
|
|
139
|
+
"generator_run_id": "f7f90f8c-d68f-41c4-9844-0c8aabc6198d",
|
|
140
|
+
"generator_runtime_source": "host-metadata",
|
|
141
|
+
"validator_runtime": "codex",
|
|
142
|
+
"validator_runtime_source": "host-metadata",
|
|
143
|
+
"minimum_independence": "different-runtime",
|
|
144
|
+
"runtime_fallback_tier": "different-runtime",
|
|
145
|
+
"blocking_severities": [
|
|
146
|
+
"Blocking"
|
|
147
|
+
]
|
|
148
|
+
},
|
|
149
|
+
"layout": {
|
|
150
|
+
"sdd_root": ".sdd",
|
|
151
|
+
"docs_root": "docs",
|
|
152
|
+
"agent_entry_path": "AGENTS.md",
|
|
153
|
+
"standing_docs_root": "docs",
|
|
154
|
+
"spec_index_path": "docs/specs/README.md",
|
|
155
|
+
"module_docs_strategy": "source-local",
|
|
156
|
+
"module_docs_relative_path": "docs/README.md",
|
|
157
|
+
"module_docs_decision": {
|
|
158
|
+
"basis": "repo-standards-default",
|
|
159
|
+
"reason": "The package has no module-local documentation folder, documentation build, or protected authored file that collides with the source-local default, so the repo-standards convention applies unchanged.",
|
|
160
|
+
"decided_by": "claude-code (bootstrap questionnaire)",
|
|
161
|
+
"decided_at": "2026-09-24T04:51:24Z"
|
|
162
|
+
},
|
|
163
|
+
"repo_context_decision": {
|
|
164
|
+
"path": ".repo-context.json",
|
|
165
|
+
"schema_path": "schemas/repo-context.schema.json",
|
|
166
|
+
"action": "create",
|
|
167
|
+
"schema_action": "create",
|
|
168
|
+
"reason": "Neither the operational context nor its schema exists in this package; both are created from the bundled Repo Standards templates and verified repository evidence.",
|
|
169
|
+
"decided_by": "claude-code (bootstrap questionnaire)",
|
|
170
|
+
"decided_at": "2026-09-24T04:51:24Z"
|
|
171
|
+
},
|
|
172
|
+
"artifact_decisions": [
|
|
173
|
+
{
|
|
174
|
+
"path": "docs/service.md",
|
|
175
|
+
"action": "omit",
|
|
176
|
+
"reason": "The package is a published library, not a deployable service; it declares no deployment descriptor, container, or runtime entry point.",
|
|
177
|
+
"decided_by": "claude-code (bootstrap questionnaire)",
|
|
178
|
+
"decided_at": "2026-09-24T04:51:24Z"
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
"path": "mkdocs.yml",
|
|
182
|
+
"action": "omit",
|
|
183
|
+
"reason": "No package-level documentation build exists; documentation publishing is owned by the surrounding workspace pipeline.",
|
|
184
|
+
"decided_by": "claude-code (bootstrap questionnaire)",
|
|
185
|
+
"decided_at": "2026-09-24T04:51:24Z"
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
"path": "api-specs/openapi.yaml",
|
|
189
|
+
"action": "omit",
|
|
190
|
+
"reason": "The package serves no HTTP routes and makes no network calls; its published surface is the npm package export described by common-timers-sdk.",
|
|
191
|
+
"decided_by": "claude-code (bootstrap questionnaire)",
|
|
192
|
+
"decided_at": "2026-09-24T04:51:24Z"
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
"path": "SECURITY.md",
|
|
196
|
+
"action": "omit",
|
|
197
|
+
"reason": "Absent at both package and workspace root; a per-package security policy would be the wrong home and no reporting contact is established.",
|
|
198
|
+
"decided_by": "claude-code (bootstrap questionnaire)",
|
|
199
|
+
"decided_at": "2026-09-24T04:51:24Z"
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"path": "CONTRIBUTING.md",
|
|
203
|
+
"action": "omit",
|
|
204
|
+
"reason": "Contribution instructions are owned at the workspace root and already linked from this package README.md.",
|
|
205
|
+
"decided_by": "claude-code (bootstrap questionnaire)",
|
|
206
|
+
"decided_at": "2026-09-24T04:51:24Z"
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"path": "CHANGELOG.md",
|
|
210
|
+
"action": "omit",
|
|
211
|
+
"reason": "Changelog generation is owned at the workspace root; no package-level changelog exists.",
|
|
212
|
+
"decided_by": "claude-code (bootstrap questionnaire)",
|
|
213
|
+
"decided_at": "2026-09-24T04:51:24Z"
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
"path": "CTO_CHECKLIST.yml",
|
|
217
|
+
"action": "omit",
|
|
218
|
+
"reason": "The CTO readiness workflow applies to deployable services; this package is a published library.",
|
|
219
|
+
"decided_by": "claude-code (bootstrap questionnaire)",
|
|
220
|
+
"decided_at": "2026-09-24T04:51:24Z"
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
"path": "schemas/cto-checklist.schema.json",
|
|
224
|
+
"action": "omit",
|
|
225
|
+
"reason": "Selected together with CTO_CHECKLIST.yml, which is omitted.",
|
|
226
|
+
"decided_by": "claude-code (bootstrap questionnaire)",
|
|
227
|
+
"decided_at": "2026-09-24T04:51:24Z"
|
|
228
|
+
}
|
|
229
|
+
],
|
|
230
|
+
"template_roots": {
|
|
231
|
+
"canonical": ".sdd/templates/repo-standards"
|
|
232
|
+
},
|
|
233
|
+
"repo_skills_root": ".sdd/skills",
|
|
234
|
+
"contract_index_path": "docs/architecture.md"
|
|
235
|
+
},
|
|
236
|
+
"substrate": {
|
|
237
|
+
"source": "WebexTools/repo-standards@d89a7fe59126ae3ad9bac9ca352aa7f12b4c85dc",
|
|
238
|
+
"consumed": [
|
|
239
|
+
"@webex/eslint-config-legacy via .eslintrc.js",
|
|
240
|
+
"@webex/babel-config-legacy via babel.config.js",
|
|
241
|
+
"@webex/jest-config-legacy via jest.config.js",
|
|
242
|
+
"@webex/legacy-tools build and test runner via package.json",
|
|
243
|
+
"package README.md"
|
|
244
|
+
],
|
|
245
|
+
"compliance_tier": "baseline"
|
|
246
|
+
},
|
|
247
|
+
"section_profiles": {
|
|
248
|
+
"repo": {
|
|
249
|
+
"owns_datastore": false,
|
|
250
|
+
"holds_client_state": false,
|
|
251
|
+
"components_interact": false,
|
|
252
|
+
"domain_data_across_components": false,
|
|
253
|
+
"caches_data": false,
|
|
254
|
+
"observability_convention": false,
|
|
255
|
+
"deploys_to_infra": false,
|
|
256
|
+
"shared_base_libs": true,
|
|
257
|
+
"is_monorepo": false,
|
|
258
|
+
"multi_platform": true,
|
|
259
|
+
"published_package": true,
|
|
260
|
+
"embedded_in_host": false,
|
|
261
|
+
"cross_repo_deps_material": false,
|
|
262
|
+
"security_arch_warranted": false,
|
|
263
|
+
"exposes_commands_or_artifacts": false
|
|
264
|
+
},
|
|
265
|
+
"resolved_by": "claude-code/claude-opus-5 (host metadata; bootstrap questionnaire)",
|
|
266
|
+
"resolved_at": "2026-09-24T04:51:24Z"
|
|
267
|
+
}
|
|
268
|
+
}
|
package/AGENTS.md
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
<!-- sdd-generated-metadata
|
|
2
|
+
doc_kind: agent-entry
|
|
3
|
+
generated_from: agents@0.3.0
|
|
4
|
+
generated_by: claude-code
|
|
5
|
+
approved_by: pending
|
|
6
|
+
updated_at: 2026-09-28T05:34:36Z
|
|
7
|
+
validation_status: pass
|
|
8
|
+
-->
|
|
9
|
+
|
|
10
|
+
# AI Agent Instructions
|
|
11
|
+
|
|
12
|
+
Instructions for AI coding assistants working on `@webex/common-timers`, the package that owns the
|
|
13
|
+
Webex JS SDK's timer primitives.
|
|
14
|
+
|
|
15
|
+
## Working Method
|
|
16
|
+
|
|
17
|
+
- Read the relevant repository files before editing. Do not edit blind.
|
|
18
|
+
- Understand the task and affected surfaces before writing code.
|
|
19
|
+
- Prefer focused edits over rewriting whole files.
|
|
20
|
+
- Do not invent commands, paths, exports, or behavior. Verify them from repository-local source.
|
|
21
|
+
- Test or validate before declaring done.
|
|
22
|
+
- Keep output concise. If something is uncertain, say so instead of guessing.
|
|
23
|
+
|
|
24
|
+
## Canonical Repository Documentation
|
|
25
|
+
|
|
26
|
+
Before changing code or documentation, start with:
|
|
27
|
+
|
|
28
|
+
- [`docs/index.md`](docs/index.md) for documentation navigation;
|
|
29
|
+
- [`docs/architecture.md`](docs/architecture.md) for package boundaries, dependencies, and the
|
|
30
|
+
contract index;
|
|
31
|
+
- [`docs/specs/README.md`](docs/specs/README.md) for the manifest-backed module and contract
|
|
32
|
+
registry;
|
|
33
|
+
- [`src/docs/README.md`](src/docs/README.md) — the module specification, and the document that
|
|
34
|
+
matters most here: it owns the public surface, the `Timer` state machine, every caller-visible
|
|
35
|
+
failure mode, and the known test gaps; and
|
|
36
|
+
- [`docs/adr/index.md`](docs/adr/index.md) for durable decisions. None are registered yet; the
|
|
37
|
+
design decisions behind the current shape live in the module spec's `Key design trade-off`.
|
|
38
|
+
|
|
39
|
+
Follow `.sdd/manifest.json` for each contract's publication state and canonical source. This package
|
|
40
|
+
publishes one contract, `common-timers-sdk`, an npm package surface whose native source is the entry
|
|
41
|
+
point declared in `package.json`; there is no OpenAPI document because the package serves no HTTP.
|
|
42
|
+
The manifest remains authoritative for canonical paths and artifact decisions.
|
|
43
|
+
|
|
44
|
+
## 1. Commands (Run First)
|
|
45
|
+
|
|
46
|
+
Run every command from the Webex JS SDK workspace root. Use only documented commands; do not invent
|
|
47
|
+
alternatives when a command is missing.
|
|
48
|
+
|
|
49
|
+
| Task | Command |
|
|
50
|
+
| ---------------------- | --------------------------------------------------- |
|
|
51
|
+
| Install dependencies | `yarn install` |
|
|
52
|
+
| Start dev environment | N/A — the package is a library with no runnable entry point |
|
|
53
|
+
| Run tests | `yarn workspace @webex/common-timers test:unit` |
|
|
54
|
+
| Run linters | `yarn workspace @webex/common-timers test:style` |
|
|
55
|
+
| Build/release artifact | `yarn workspace @webex/common-timers build:src` |
|
|
56
|
+
| Format code | N/A — no package-local format command; Prettier configuration is owned at the workspace root |
|
|
57
|
+
|
|
58
|
+
Do not run `yarn workspace @webex/common-timers test`. The aggregate `test` script chains
|
|
59
|
+
`test:integration` and `test:browser`, and this package declares neither, so the command cannot
|
|
60
|
+
succeed. If a required command is unknown, stop and ask for the exact command.
|
|
61
|
+
|
|
62
|
+
## 2. Agent Persona and Scope
|
|
63
|
+
|
|
64
|
+
You are the SDK foundation-utilities engineering assistant for `@webex/common-timers`.
|
|
65
|
+
|
|
66
|
+
Primary outcomes:
|
|
67
|
+
|
|
68
|
+
- Deliver production-safe changes with passing tests.
|
|
69
|
+
- Preserve existing behavior unless a change request explicitly requires a behavior change.
|
|
70
|
+
- Keep docs and configuration aligned with code changes.
|
|
71
|
+
|
|
72
|
+
Definition of done:
|
|
73
|
+
|
|
74
|
+
1. Requested change is implemented.
|
|
75
|
+
2. `test:unit` and `test:style` pass locally.
|
|
76
|
+
3. Updated docs cover new behavior — a behavior change updates `src/docs/README.md` in the same
|
|
77
|
+
commit.
|
|
78
|
+
4. PR summary includes risks and rollback notes, naming every dependent package the change could
|
|
79
|
+
affect.
|
|
80
|
+
|
|
81
|
+
## 3. Repository Knowledge
|
|
82
|
+
|
|
83
|
+
### Tech Stack
|
|
84
|
+
|
|
85
|
+
| Area | Tooling | Version |
|
|
86
|
+
| ---------------- | --------------------------------------------------- | ------------------------------------------ |
|
|
87
|
+
| Language runtime | Node.js | `>=18` per `package.json` `engines` |
|
|
88
|
+
| Language | TypeScript source, transpiled by Babel | No local `tsconfig.json` and no TypeScript dependency; declarations are emitted by the `-ts` flag of the build command |
|
|
89
|
+
| Build tool | `webex-legacy-tools` via `@webex/legacy-tools` | `workspace:*` |
|
|
90
|
+
| Test framework | jest via `@webex/jest-config-legacy`, with sinon fake timers | `workspace:*` |
|
|
91
|
+
| Lint | eslint via `@webex/eslint-config-legacy` | eslint `^8.24.0` |
|
|
92
|
+
| Format | prettier | `^2.7.1` (declared, no local script) |
|
|
93
|
+
|
|
94
|
+
### Project Map
|
|
95
|
+
|
|
96
|
+
- `src/index.ts`: the entire module — `safeSetTimeout`, `safeSetInterval`, and `Timer`. There is no
|
|
97
|
+
other source file.
|
|
98
|
+
- `src/docs/README.md`: the module specification. Update it in the same change that alters behavior.
|
|
99
|
+
- `test/unit/spec/index.ts`: intended-behaviour tests mirroring `src/`. 13 jest cases, and the only
|
|
100
|
+
test file in the package. This module has no characterization baseline.
|
|
101
|
+
- `docs/`: package-wide standing documentation (index, architecture, getting started, spec registry,
|
|
102
|
+
ADR index).
|
|
103
|
+
- `.sdd/`: the SDD manifest and the hidden Repo Standards template snapshot. Never publish templates
|
|
104
|
+
from here into `docs/`.
|
|
105
|
+
- `process`: a one-line browser shim resolved by the browserify transform when bundling for a browser.
|
|
106
|
+
- `jest.config.js`, `babel.config.js`, `.eslintrc.js`: two-line files that re-export shared workspace
|
|
107
|
+
configuration. There is no package-local knob — changing build, lint, or test behavior means
|
|
108
|
+
changing the shared config.
|
|
109
|
+
|
|
110
|
+
### Critical Constraints
|
|
111
|
+
|
|
112
|
+
- **Every timer created through this package is unref-ed where the platform supports it.** That is
|
|
113
|
+
the package's entire reason to exist. Never add a code path that returns a handle without the
|
|
114
|
+
capability check, and never add an opt-out without treating it as a breaking change.
|
|
115
|
+
- **`unref` is feature-detected on the handle, never inferred from the platform.** One module and one
|
|
116
|
+
build serve both Node and the browser. Do not introduce a platform branch, a conditional export, a
|
|
117
|
+
`browser` field, or a polyfill.
|
|
118
|
+
- **The exported surface is consumed by eleven workspace packages plus external npm consumers.** Any
|
|
119
|
+
change to `safeSetTimeout`, `safeSetInterval`, or `Timer` — including the `number | NodeJS.Timeout`
|
|
120
|
+
return union — is a breaking change until proven otherwise.
|
|
121
|
+
- **Do not size that blast radius from `package.json` files alone.** The declared and importing sets
|
|
122
|
+
differ in both directions: `@webex/internal-plugin-scheduler` declares the dependency but never
|
|
123
|
+
imports it, and `@webex/plugin-meetings` imports both wrappers in `src/meeting/index.ts` without
|
|
124
|
+
declaring the dependency at all. Grep the source for `@webex/common-timers` as well as reading
|
|
125
|
+
manifests, or you will miss a real consumer. Do not "fix" the missing `plugin-meetings`
|
|
126
|
+
declaration as a side effect of work in this package — raise it with that package's owners.
|
|
127
|
+
- **`Timer` throws on every invalid transition, and `done` is terminal.** Do not soften a throw into
|
|
128
|
+
a no-op; consumers rely on the failure being loud. The thrown message wording is asserted by
|
|
129
|
+
regular expression in the unit suite and is part of the contract.
|
|
130
|
+
- **`reset()` restores the full timeout, not the remaining time.** It is an idle deadline. Do not
|
|
131
|
+
"fix" it into a pause/resume.
|
|
132
|
+
- **The package has zero runtime dependencies.** Adding one changes the package's architectural
|
|
133
|
+
character and must be escalated, not assumed.
|
|
134
|
+
|
|
135
|
+
## 4. Testing Workflow
|
|
136
|
+
|
|
137
|
+
Run checks in this order:
|
|
138
|
+
|
|
139
|
+
1. `yarn workspace @webex/common-timers test:unit`
|
|
140
|
+
2. `yarn workspace @webex/common-timers test:style`
|
|
141
|
+
3. N/A — the package declares no integration or end-to-end tier.
|
|
142
|
+
|
|
143
|
+
Testing rules:
|
|
144
|
+
|
|
145
|
+
- Add or update tests for every behavior change.
|
|
146
|
+
- Cover failure paths and edge cases, not only happy paths. Every rejected `Timer` transition already
|
|
147
|
+
has a test; keep it that way.
|
|
148
|
+
- Use `sinon.useFakeTimers()` to control time, as the existing suite does. Do not introduce real
|
|
149
|
+
waits. If you add `unref` assertions, note that sinon's fake handles expose `unref()` but no
|
|
150
|
+
observable ref state, so such a test must restore the clock and assert `hasRef()` on a real handle.
|
|
151
|
+
- **The `unref` contract is not covered by any test.** Removing the `unref()` call from
|
|
152
|
+
`src/index.ts` leaves the suite green, so a green run does not prove the package still does the one
|
|
153
|
+
thing it exists for. Verify that behaviour by reading `src/index.ts` until assertions exist.
|
|
154
|
+
- **Rebuild before trusting a green run.** The suite imports the package by name, which resolves
|
|
155
|
+
through `main` to `dist/`. It validates the built artifact, so run
|
|
156
|
+
`yarn workspace @webex/common-timers build:src` after a source change or you will be testing the
|
|
157
|
+
previous build.
|
|
158
|
+
- Do not remove tests to make CI pass.
|
|
159
|
+
|
|
160
|
+
## 5. Code Style and Patterns
|
|
161
|
+
|
|
162
|
+
Coding expectations:
|
|
163
|
+
|
|
164
|
+
- Follow repository style rules and static analysis output.
|
|
165
|
+
- Prefer small, composable functions over large procedural blocks.
|
|
166
|
+
- Make error handling explicit at system boundaries.
|
|
167
|
+
- Keep public interfaces stable unless a breaking change is requested.
|
|
168
|
+
- Before adding a literal path, filename, command, workflow identifier, status, or other policy
|
|
169
|
+
value, search the repository for exact and semantically equivalent uses. When repeated production
|
|
170
|
+
values represent one shared contract, move them to the narrowest owning shared module or catalog as
|
|
171
|
+
one canonical named constant, enum, or type and update consumers. Keep incidental similarities,
|
|
172
|
+
one-off implementation details, and independent test fixtures local.
|
|
173
|
+
- Do not import a default, named, object, or namespace binding and immediately re-export it.
|
|
174
|
+
Consumers should import from the owning module; package barrels should use direct named re-exports.
|
|
175
|
+
- Do not write inline runtime `typeof` checks outside descriptively named guard implementations.
|
|
176
|
+
Import and reuse the owning guard instead; TypeScript type-position `typeof` queries remain
|
|
177
|
+
allowed. The existing `if (timer.unref)` capability check is a deliberate exception: it tests for a
|
|
178
|
+
platform method on a returned handle, not a type.
|
|
179
|
+
|
|
180
|
+
Pattern example:
|
|
181
|
+
|
|
182
|
+
```text
|
|
183
|
+
Preferred:
|
|
184
|
+
- Feature-detect a platform capability on the value you were handed.
|
|
185
|
+
- Return the platform handle unchanged so callers keep using clearTimeout/clearInterval.
|
|
186
|
+
- Throw with a message that names the operation and the rejecting state.
|
|
187
|
+
|
|
188
|
+
Avoid:
|
|
189
|
+
- Branching on the platform instead of the capability.
|
|
190
|
+
- Wrapping or normalizing the handle into a new type.
|
|
191
|
+
- Turning an invalid lifecycle call into a silent no-op.
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## 6. Git Workflow
|
|
195
|
+
|
|
196
|
+
- Branch naming: `[feature|fix|chore]/[short-description]`
|
|
197
|
+
- Commit format: conventional commits scoped to the package, for example
|
|
198
|
+
`fix(common-timers): ...` or `docs(common-timers): ...`. The workspace enforces the format with
|
|
199
|
+
commitlint.
|
|
200
|
+
- Keep commits focused and reversible.
|
|
201
|
+
- Rebase on the target branch before opening a PR.
|
|
202
|
+
|
|
203
|
+
PR checklist:
|
|
204
|
+
|
|
205
|
+
- What changed and why.
|
|
206
|
+
- Tests run and results.
|
|
207
|
+
- Risk/impact areas — name the dependent packages a surface change reaches.
|
|
208
|
+
- Rollback approach.
|
|
209
|
+
|
|
210
|
+
## 7. Boundaries and Escalation
|
|
211
|
+
|
|
212
|
+
### Always
|
|
213
|
+
|
|
214
|
+
- Prefer existing patterns over introducing new architecture.
|
|
215
|
+
- Keep changes minimal for the requested scope.
|
|
216
|
+
- Call out assumptions and unknowns explicitly.
|
|
217
|
+
- Update `src/docs/README.md` in the same change as a behavior change.
|
|
218
|
+
|
|
219
|
+
### Ask First
|
|
220
|
+
|
|
221
|
+
- Adding any runtime dependency — the package currently has none.
|
|
222
|
+
- Changing the exported surface, the return type union, or a thrown message.
|
|
223
|
+
- Adding a platform branch, conditional export, or polyfill.
|
|
224
|
+
- Making `unref` optional or conditional on anything other than the handle's own capability.
|
|
225
|
+
- Large refactors of the `Timer` lifecycle.
|
|
226
|
+
- Adding validation to the `Timer` constructor. There is a known, already-triaged follow-up here:
|
|
227
|
+
the constructor accepts `undefined`, `null`, `0`, negative numbers and `NaN`, all of which
|
|
228
|
+
produce a timer that fires immediately. It was triaged on 2026-09-24 as fix-separately because
|
|
229
|
+
rejecting those values is breaking for any caller relying on the coercion. If you are picking
|
|
230
|
+
that work up, it needs its own requirement, its own error-mode rows, and tests covering the
|
|
231
|
+
rejected durations — see the module spec's Pitfalls and constraints. No test currently pins the
|
|
232
|
+
present behaviour, so changing it will not turn the suite red on its own.
|
|
233
|
+
|
|
234
|
+
### Never
|
|
235
|
+
|
|
236
|
+
- Commit secrets, keys, or credentials.
|
|
237
|
+
- Bypass required checks by disabling tests or linters.
|
|
238
|
+
- Rewrite history on shared branches.
|
|
239
|
+
- Change unrelated files "while here" without clear need.
|
|
240
|
+
- Publish a Repo Standards template out of `.sdd/templates/` into `docs/`.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<!-- sdd-generated-metadata
|
|
2
|
+
doc_kind: standing-doc
|
|
3
|
+
generated_from: adr-index@0.3.0
|
|
4
|
+
generated_by: claude-code
|
|
5
|
+
approved_by: pending
|
|
6
|
+
updated_at: 2026-09-28T05:34:36Z
|
|
7
|
+
validation_status: pass
|
|
8
|
+
-->
|
|
9
|
+
|
|
10
|
+
# Architectural decision records
|
|
11
|
+
|
|
12
|
+
Numbered, append-only records of structural decisions affecting `@webex/common-timers` and its
|
|
13
|
+
documentation. Add a concrete ADR (for example, `0001-some-decision.md`) when a decision is
|
|
14
|
+
non-obvious, hard to reverse, or worth explaining to a future contributor. Do not edit an accepted
|
|
15
|
+
decision in place when its outcome changes; supersede it with a new ADR and link both records.
|
|
16
|
+
|
|
17
|
+
## Index
|
|
18
|
+
|
|
19
|
+
No architectural decisions are registered yet. Replace this sentence with links to concrete,
|
|
20
|
+
accepted ADRs as decisions are recorded.
|
|
21
|
+
|
|
22
|
+
The package does carry design decisions worth preserving — unref-ing every timer unconditionally,
|
|
23
|
+
feature-detecting `unref` on the handle instead of branching on the platform, and making `Timer`
|
|
24
|
+
throw on an invalid transition rather than ignoring it. They are recorded in the module
|
|
25
|
+
specification's [`Key design trade-off`](../../src/docs/README.md#key-design-trade-off) section
|
|
26
|
+
rather than as ADRs, because the package's commit history does not identify the deciders or decision
|
|
27
|
+
dates an ADR record requires. Promote any of them to a numbered ADR once that provenance is
|
|
28
|
+
established.
|
|
29
|
+
|
|
30
|
+
Add new entries as ADRs are accepted. Keep this list in numeric order.
|
|
31
|
+
|
|
32
|
+
Each ADR should state its context, decision, alternatives, consequences, and the condition that would
|
|
33
|
+
cause the team to revisit it. Record the deciders, any superseded decision, and the constraint future
|
|
34
|
+
changes must preserve.
|