@osdlc/tiered-dirs 0.0.0-stage → 0.3.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/CHANGELOG.md +99 -0
- package/CONTRIBUTING.md +66 -0
- package/LICENSE-APACHE +202 -0
- package/LICENSE-MIT +21 -0
- package/README.md +422 -2
- package/SECURITY.md +23 -0
- package/dist/base-dirs.d.ts +311 -0
- package/dist/base-dirs.js +477 -0
- package/dist/errors.d.ts +36 -0
- package/dist/errors.js +20 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +24 -0
- package/dist/segment.d.ts +17 -0
- package/dist/segment.js +40 -0
- package/dist/tiered-dirs.d.ts +190 -0
- package/dist/tiered-dirs.js +237 -0
- package/package.json +64 -3
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import type { BaseDirs, EnvVars, Home, ResolveOptions } from './base-dirs.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Which method produced a {@link Tier}.
|
|
4
|
+
*
|
|
5
|
+
* Exposed so a caller — or a gate run over a third party's install — can tell
|
|
6
|
+
* a suite address from a standalone one without parsing the segment string.
|
|
7
|
+
*
|
|
8
|
+
* A minor release may add a kind, as the Rust `TierKind` is
|
|
9
|
+
* `#[non_exhaustive]`. A `switch` over it needs a `default` arm.
|
|
10
|
+
*
|
|
11
|
+
* Stable in 0.x.
|
|
12
|
+
*/
|
|
13
|
+
export type TierKind = 'vendor' | 'suite' | 'standalone';
|
|
14
|
+
/**
|
|
15
|
+
* Every {@link TierKind}.
|
|
16
|
+
*
|
|
17
|
+
* Stable in 0.x.
|
|
18
|
+
*/
|
|
19
|
+
export declare const TIER_KINDS: readonly TierKind[];
|
|
20
|
+
/**
|
|
21
|
+
* Whether a bundle identifier sits under `vendor`: a bare prefix test. The
|
|
22
|
+
* Rust twin's `is_vendor_bundle_id` is the same test. Nothing stricter: a
|
|
23
|
+
* dot-boundary rule would make the twins disagree about which apps conform.
|
|
24
|
+
*
|
|
25
|
+
* For a caller with no {@link TieredDirs}. The Rust twin also has a
|
|
26
|
+
* debug-only assert form; TypeScript has no debug build, so the caller
|
|
27
|
+
* decides what to do with the answer.
|
|
28
|
+
*
|
|
29
|
+
* Stable in 0.x.
|
|
30
|
+
*/
|
|
31
|
+
export declare function isVendorBundleId(vendor: string, bundleId: string): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* A directory addressed by tier and home — or the untiered scratch root — plus
|
|
34
|
+
* any scope and app segments below it. Immutable: every builder method returns
|
|
35
|
+
* a new value.
|
|
36
|
+
*
|
|
37
|
+
* Exported from the package as a type only. Get one from `Tier.home`,
|
|
38
|
+
* `TieredDirs.scratch()`, `Dir.scoped` or `Dir.app`, which check their input.
|
|
39
|
+
*
|
|
40
|
+
* Stable in 0.x.
|
|
41
|
+
*/
|
|
42
|
+
export declare class Dir {
|
|
43
|
+
#private;
|
|
44
|
+
/** The resolved root this path hangs off: a home's base, or scratch. */
|
|
45
|
+
readonly base: string;
|
|
46
|
+
/** Validated segments below {@link base}, outermost first. */
|
|
47
|
+
get segments(): readonly string[];
|
|
48
|
+
constructor(base: string, segments: readonly string[]);
|
|
49
|
+
/** The absolute directory, joined with the host's separator. */
|
|
50
|
+
get path(): string;
|
|
51
|
+
/**
|
|
52
|
+
* The segments below the base, always `/`-separated.
|
|
53
|
+
*
|
|
54
|
+
* This is the value the parity fixture pins: the Rust twin and this one must
|
|
55
|
+
* produce the same string here for the same input.
|
|
56
|
+
*/
|
|
57
|
+
get relative(): string;
|
|
58
|
+
/**
|
|
59
|
+
* Key this location by a content root's id:
|
|
60
|
+
* `.scoped('vaults', vaultId)` appends `vaults/<vaultId>`.
|
|
61
|
+
*
|
|
62
|
+
* Both arguments are validated as single path segments.
|
|
63
|
+
*/
|
|
64
|
+
scoped(kind: string, key: string): Dir;
|
|
65
|
+
/** Append one app's own directory. Validated as a single path segment. */
|
|
66
|
+
app(app: string): Dir;
|
|
67
|
+
toString(): string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* One rung of the vertical axis — who shares the state — bound to a set of
|
|
71
|
+
* base directories. Pick a home off it to get a directory.
|
|
72
|
+
*
|
|
73
|
+
* Tiers are distinct addresses. `vendor().durable()`, `suite(k).durable()` and
|
|
74
|
+
* `standalone(t).durable()` are different locations and the SDK never reads two
|
|
75
|
+
* and merges them. A given piece of state lives at exactly one tier and the
|
|
76
|
+
* caller names the tier it means. "Look up both" is something a specific
|
|
77
|
+
* reader does deliberately, per file, at its own call site — never a per-key
|
|
78
|
+
* cascade baked in here, which starts as two lines and ends as deep-merge
|
|
79
|
+
* with array-override rules nobody can predict.
|
|
80
|
+
*
|
|
81
|
+
* Exported from the package as a type only. Get one from `TieredDirs.vendor()`,
|
|
82
|
+
* `suite(key)` or `standalone(tool)`, which check their input.
|
|
83
|
+
*
|
|
84
|
+
* Stable in 0.x.
|
|
85
|
+
*/
|
|
86
|
+
export declare class Tier {
|
|
87
|
+
#private;
|
|
88
|
+
/** The directory segment this tier contributes, e.g. `com.example.notes`. */
|
|
89
|
+
readonly segment: string;
|
|
90
|
+
/** Which method produced this tier (`vendor`, `suite` or `standalone`). */
|
|
91
|
+
readonly kind: TierKind;
|
|
92
|
+
constructor(bases: BaseDirs, kind: TierKind, segment: string);
|
|
93
|
+
/** Long-lived state that must survive a cache purge. */
|
|
94
|
+
durable(): Dir;
|
|
95
|
+
/** User-editable configuration. */
|
|
96
|
+
config(): Dir;
|
|
97
|
+
/** Regenerable data. The OS may delete this at any time. */
|
|
98
|
+
cache(): Dir;
|
|
99
|
+
/** State that should outlive a restart but is not user data. */
|
|
100
|
+
state(): Dir;
|
|
101
|
+
/** Log files. Never under `cache` — the OS purges cache. */
|
|
102
|
+
logs(): Dir;
|
|
103
|
+
/** Pidfiles, sockets, locks. Never under `cache`. */
|
|
104
|
+
runtime(): Dir;
|
|
105
|
+
/** The same six homes, named dynamically. Table-driven callers use this. */
|
|
106
|
+
home(home: Home): Dir;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* The entry point: a vendor and base directories resolved once, then the tiers
|
|
110
|
+
* off them.
|
|
111
|
+
*
|
|
112
|
+
* ```ts
|
|
113
|
+
* const dirs = TieredDirs.fromEnv('com.example', envVars('EXAMPLE'))
|
|
114
|
+
* dirs.suite('notes').cache().scoped('vaults', vaultId).app('search').path
|
|
115
|
+
* // <cache>/com.example.notes/vaults/<vaultId>/search
|
|
116
|
+
* ```
|
|
117
|
+
*
|
|
118
|
+
* The product tier — one product's own directory, keyed by bundle id — is not
|
|
119
|
+
* here. An app framework usually resolves it, and this package takes no
|
|
120
|
+
* framework dependency.
|
|
121
|
+
*
|
|
122
|
+
* Stable in 0.x.
|
|
123
|
+
*/
|
|
124
|
+
export declare class TieredDirs {
|
|
125
|
+
/** The vendor segment: the vendor tier, and the prefix of every suite. */
|
|
126
|
+
readonly vendorName: string;
|
|
127
|
+
/** The roots every path in this instance hangs off. Resolved at construction. */
|
|
128
|
+
readonly bases: Readonly<BaseDirs>;
|
|
129
|
+
/**
|
|
130
|
+
* Build from injected roots — the seam a server that cannot read the
|
|
131
|
+
* environment uses, handing in roots from its own config.
|
|
132
|
+
*
|
|
133
|
+
* Throws {@link TieredDirsError} with code `INVALID_KEY` unless `vendor` is
|
|
134
|
+
* a single path segment.
|
|
135
|
+
*/
|
|
136
|
+
constructor(vendor: string, bases: BaseDirs);
|
|
137
|
+
/**
|
|
138
|
+
* Build from the environment, reading it exactly once through
|
|
139
|
+
* {@link systemBaseDirs}. Every input is injectable, so a test asserts exact
|
|
140
|
+
* paths without a real home directory. `opts.convention` picks the per-OS
|
|
141
|
+
* table: `native` (the default) or `xdg`.
|
|
142
|
+
*
|
|
143
|
+
* Throws {@link TieredDirsError} as {@link systemBaseDirs} does, and with
|
|
144
|
+
* code `INVALID_KEY` unless `vendor` is a single path segment.
|
|
145
|
+
*/
|
|
146
|
+
static fromEnv(vendor: string, vars: EnvVars, opts?: ResolveOptions): TieredDirs;
|
|
147
|
+
/** Shared by every product line the vendor ships: `<vendor>`. */
|
|
148
|
+
vendor(): Tier;
|
|
149
|
+
/**
|
|
150
|
+
* Shared by the products that share user data: `<vendor>.<suiteKey>`.
|
|
151
|
+
*
|
|
152
|
+
* `suiteKey` is a caller-supplied constant, not something derived from a
|
|
153
|
+
* bundle id.
|
|
154
|
+
*/
|
|
155
|
+
suite(suiteKey: string): Tier;
|
|
156
|
+
/**
|
|
157
|
+
* One general tool's own root: the bare `tool` name, with no vendor segment.
|
|
158
|
+
*
|
|
159
|
+
* For a tool the vendor ships into other people's repositories. It is not
|
|
160
|
+
* part of the vendor's suite, so it must not write under the vendor's
|
|
161
|
+
* namespace. Its config directory is `~/.config/<tool>` on Linux and under
|
|
162
|
+
* `xdg` on macOS, and `~/Library/Application Support/<tool>` under `native`
|
|
163
|
+
* on macOS.
|
|
164
|
+
*
|
|
165
|
+
* Use {@link TieredDirs.suite} for anything that ships as part of the suite.
|
|
166
|
+
* The two are different addresses and nothing merges them.
|
|
167
|
+
*/
|
|
168
|
+
standalone(tool: string): Tier;
|
|
169
|
+
/**
|
|
170
|
+
* Whether a bundle identifier sits under this vendor.
|
|
171
|
+
*
|
|
172
|
+
* {@link isVendorBundleId} with this vendor. A migration tracker, not a
|
|
173
|
+
* gate: TS has no debug assertion, so the caller decides what to do with
|
|
174
|
+
* the answer.
|
|
175
|
+
*/
|
|
176
|
+
isVendorBundleId(bundleId: string): boolean;
|
|
177
|
+
/**
|
|
178
|
+
* The platform temp directory, untiered: state that does not outlive the
|
|
179
|
+
* call that wrote it.
|
|
180
|
+
*
|
|
181
|
+
* A thin wrapper over the platform temp dir. It adds nothing, and that is
|
|
182
|
+
* its whole value: with it, "no direct OS path call outside this package" is
|
|
183
|
+
* a greppable invariant instead of a rule with exceptions.
|
|
184
|
+
*
|
|
185
|
+
* Not {@link Tier.runtime}. Runtime holds state that does outlive the call
|
|
186
|
+
* and describes a live process — a pidfile, a socket, a lock — and the OS
|
|
187
|
+
* sweeps temp while such a process is still running.
|
|
188
|
+
*/
|
|
189
|
+
scratch(): Dir;
|
|
190
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { systemBaseDirs } from "./base-dirs.js";
|
|
3
|
+
import { assertSegment } from "./segment.js";
|
|
4
|
+
/**
|
|
5
|
+
* Every {@link TierKind}.
|
|
6
|
+
*
|
|
7
|
+
* Stable in 0.x.
|
|
8
|
+
*/
|
|
9
|
+
export const TIER_KINDS = ['vendor', 'suite', 'standalone'];
|
|
10
|
+
/**
|
|
11
|
+
* Whether a bundle identifier sits under `vendor`: a bare prefix test. The
|
|
12
|
+
* Rust twin's `is_vendor_bundle_id` is the same test. Nothing stricter: a
|
|
13
|
+
* dot-boundary rule would make the twins disagree about which apps conform.
|
|
14
|
+
*
|
|
15
|
+
* For a caller with no {@link TieredDirs}. The Rust twin also has a
|
|
16
|
+
* debug-only assert form; TypeScript has no debug build, so the caller
|
|
17
|
+
* decides what to do with the answer.
|
|
18
|
+
*
|
|
19
|
+
* Stable in 0.x.
|
|
20
|
+
*/
|
|
21
|
+
export function isVendorBundleId(vendor, bundleId) {
|
|
22
|
+
return bundleId.startsWith(vendor);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* A directory addressed by tier and home — or the untiered scratch root — plus
|
|
26
|
+
* any scope and app segments below it. Immutable: every builder method returns
|
|
27
|
+
* a new value.
|
|
28
|
+
*
|
|
29
|
+
* Exported from the package as a type only. Get one from `Tier.home`,
|
|
30
|
+
* `TieredDirs.scratch()`, `Dir.scoped` or `Dir.app`, which check their input.
|
|
31
|
+
*
|
|
32
|
+
* Stable in 0.x.
|
|
33
|
+
*/
|
|
34
|
+
export class Dir {
|
|
35
|
+
/** The resolved root this path hangs off: a home's base, or scratch. */
|
|
36
|
+
base;
|
|
37
|
+
/** Validated segments below {@link base}, outermost first. */
|
|
38
|
+
get segments() {
|
|
39
|
+
return this.#segments;
|
|
40
|
+
}
|
|
41
|
+
// A private field makes the type nominal: an object literal is not a `Dir`.
|
|
42
|
+
#segments;
|
|
43
|
+
constructor(base, segments) {
|
|
44
|
+
this.base = base;
|
|
45
|
+
this.#segments = Object.freeze([...segments]);
|
|
46
|
+
}
|
|
47
|
+
/** The absolute directory, joined with the host's separator. */
|
|
48
|
+
get path() {
|
|
49
|
+
return this.segments.length === 0 ? this.base : join(this.base, ...this.segments);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The segments below the base, always `/`-separated.
|
|
53
|
+
*
|
|
54
|
+
* This is the value the parity fixture pins: the Rust twin and this one must
|
|
55
|
+
* produce the same string here for the same input.
|
|
56
|
+
*/
|
|
57
|
+
get relative() {
|
|
58
|
+
return this.segments.join('/');
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Key this location by a content root's id:
|
|
62
|
+
* `.scoped('vaults', vaultId)` appends `vaults/<vaultId>`.
|
|
63
|
+
*
|
|
64
|
+
* Both arguments are validated as single path segments.
|
|
65
|
+
*/
|
|
66
|
+
scoped(kind, key) {
|
|
67
|
+
assertSegment('scope kind', kind);
|
|
68
|
+
assertSegment('scope', key);
|
|
69
|
+
return new Dir(this.base, [...this.segments, kind, key]);
|
|
70
|
+
}
|
|
71
|
+
/** Append one app's own directory. Validated as a single path segment. */
|
|
72
|
+
app(app) {
|
|
73
|
+
assertSegment('app', app);
|
|
74
|
+
return new Dir(this.base, [...this.segments, app]);
|
|
75
|
+
}
|
|
76
|
+
toString() {
|
|
77
|
+
return this.path;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* One rung of the vertical axis — who shares the state — bound to a set of
|
|
82
|
+
* base directories. Pick a home off it to get a directory.
|
|
83
|
+
*
|
|
84
|
+
* Tiers are distinct addresses. `vendor().durable()`, `suite(k).durable()` and
|
|
85
|
+
* `standalone(t).durable()` are different locations and the SDK never reads two
|
|
86
|
+
* and merges them. A given piece of state lives at exactly one tier and the
|
|
87
|
+
* caller names the tier it means. "Look up both" is something a specific
|
|
88
|
+
* reader does deliberately, per file, at its own call site — never a per-key
|
|
89
|
+
* cascade baked in here, which starts as two lines and ends as deep-merge
|
|
90
|
+
* with array-override rules nobody can predict.
|
|
91
|
+
*
|
|
92
|
+
* Exported from the package as a type only. Get one from `TieredDirs.vendor()`,
|
|
93
|
+
* `suite(key)` or `standalone(tool)`, which check their input.
|
|
94
|
+
*
|
|
95
|
+
* Stable in 0.x.
|
|
96
|
+
*/
|
|
97
|
+
export class Tier {
|
|
98
|
+
/** The directory segment this tier contributes, e.g. `com.example.notes`. */
|
|
99
|
+
segment;
|
|
100
|
+
/** Which method produced this tier (`vendor`, `suite` or `standalone`). */
|
|
101
|
+
kind;
|
|
102
|
+
#bases;
|
|
103
|
+
constructor(bases, kind, segment) {
|
|
104
|
+
this.#bases = bases;
|
|
105
|
+
this.kind = kind;
|
|
106
|
+
this.segment = segment;
|
|
107
|
+
}
|
|
108
|
+
/** Long-lived state that must survive a cache purge. */
|
|
109
|
+
durable() {
|
|
110
|
+
return this.home('durable');
|
|
111
|
+
}
|
|
112
|
+
/** User-editable configuration. */
|
|
113
|
+
config() {
|
|
114
|
+
return this.home('config');
|
|
115
|
+
}
|
|
116
|
+
/** Regenerable data. The OS may delete this at any time. */
|
|
117
|
+
cache() {
|
|
118
|
+
return this.home('cache');
|
|
119
|
+
}
|
|
120
|
+
/** State that should outlive a restart but is not user data. */
|
|
121
|
+
state() {
|
|
122
|
+
return this.home('state');
|
|
123
|
+
}
|
|
124
|
+
/** Log files. Never under `cache` — the OS purges cache. */
|
|
125
|
+
logs() {
|
|
126
|
+
return this.home('logs');
|
|
127
|
+
}
|
|
128
|
+
/** Pidfiles, sockets, locks. Never under `cache`. */
|
|
129
|
+
runtime() {
|
|
130
|
+
return this.home('runtime');
|
|
131
|
+
}
|
|
132
|
+
/** The same six homes, named dynamically. Table-driven callers use this. */
|
|
133
|
+
home(home) {
|
|
134
|
+
return new Dir(this.#bases[home], [this.segment]);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* The entry point: a vendor and base directories resolved once, then the tiers
|
|
139
|
+
* off them.
|
|
140
|
+
*
|
|
141
|
+
* ```ts
|
|
142
|
+
* const dirs = TieredDirs.fromEnv('com.example', envVars('EXAMPLE'))
|
|
143
|
+
* dirs.suite('notes').cache().scoped('vaults', vaultId).app('search').path
|
|
144
|
+
* // <cache>/com.example.notes/vaults/<vaultId>/search
|
|
145
|
+
* ```
|
|
146
|
+
*
|
|
147
|
+
* The product tier — one product's own directory, keyed by bundle id — is not
|
|
148
|
+
* here. An app framework usually resolves it, and this package takes no
|
|
149
|
+
* framework dependency.
|
|
150
|
+
*
|
|
151
|
+
* Stable in 0.x.
|
|
152
|
+
*/
|
|
153
|
+
export class TieredDirs {
|
|
154
|
+
/** The vendor segment: the vendor tier, and the prefix of every suite. */
|
|
155
|
+
vendorName;
|
|
156
|
+
/** The roots every path in this instance hangs off. Resolved at construction. */
|
|
157
|
+
bases;
|
|
158
|
+
/**
|
|
159
|
+
* Build from injected roots — the seam a server that cannot read the
|
|
160
|
+
* environment uses, handing in roots from its own config.
|
|
161
|
+
*
|
|
162
|
+
* Throws {@link TieredDirsError} with code `INVALID_KEY` unless `vendor` is
|
|
163
|
+
* a single path segment.
|
|
164
|
+
*/
|
|
165
|
+
constructor(vendor, bases) {
|
|
166
|
+
assertSegment('vendor', vendor);
|
|
167
|
+
this.vendorName = vendor;
|
|
168
|
+
this.bases = Object.freeze({ ...bases });
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Build from the environment, reading it exactly once through
|
|
172
|
+
* {@link systemBaseDirs}. Every input is injectable, so a test asserts exact
|
|
173
|
+
* paths without a real home directory. `opts.convention` picks the per-OS
|
|
174
|
+
* table: `native` (the default) or `xdg`.
|
|
175
|
+
*
|
|
176
|
+
* Throws {@link TieredDirsError} as {@link systemBaseDirs} does, and with
|
|
177
|
+
* code `INVALID_KEY` unless `vendor` is a single path segment.
|
|
178
|
+
*/
|
|
179
|
+
static fromEnv(vendor, vars, opts = {}) {
|
|
180
|
+
return new TieredDirs(vendor, systemBaseDirs(vars, opts));
|
|
181
|
+
}
|
|
182
|
+
/** Shared by every product line the vendor ships: `<vendor>`. */
|
|
183
|
+
vendor() {
|
|
184
|
+
return new Tier(this.bases, 'vendor', this.vendorName);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Shared by the products that share user data: `<vendor>.<suiteKey>`.
|
|
188
|
+
*
|
|
189
|
+
* `suiteKey` is a caller-supplied constant, not something derived from a
|
|
190
|
+
* bundle id.
|
|
191
|
+
*/
|
|
192
|
+
suite(suiteKey) {
|
|
193
|
+
assertSegment('suite', suiteKey);
|
|
194
|
+
return new Tier(this.bases, 'suite', `${this.vendorName}.${suiteKey}`);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* One general tool's own root: the bare `tool` name, with no vendor segment.
|
|
198
|
+
*
|
|
199
|
+
* For a tool the vendor ships into other people's repositories. It is not
|
|
200
|
+
* part of the vendor's suite, so it must not write under the vendor's
|
|
201
|
+
* namespace. Its config directory is `~/.config/<tool>` on Linux and under
|
|
202
|
+
* `xdg` on macOS, and `~/Library/Application Support/<tool>` under `native`
|
|
203
|
+
* on macOS.
|
|
204
|
+
*
|
|
205
|
+
* Use {@link TieredDirs.suite} for anything that ships as part of the suite.
|
|
206
|
+
* The two are different addresses and nothing merges them.
|
|
207
|
+
*/
|
|
208
|
+
standalone(tool) {
|
|
209
|
+
assertSegment('standalone', tool);
|
|
210
|
+
return new Tier(this.bases, 'standalone', tool);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Whether a bundle identifier sits under this vendor.
|
|
214
|
+
*
|
|
215
|
+
* {@link isVendorBundleId} with this vendor. A migration tracker, not a
|
|
216
|
+
* gate: TS has no debug assertion, so the caller decides what to do with
|
|
217
|
+
* the answer.
|
|
218
|
+
*/
|
|
219
|
+
isVendorBundleId(bundleId) {
|
|
220
|
+
return isVendorBundleId(this.vendorName, bundleId);
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* The platform temp directory, untiered: state that does not outlive the
|
|
224
|
+
* call that wrote it.
|
|
225
|
+
*
|
|
226
|
+
* A thin wrapper over the platform temp dir. It adds nothing, and that is
|
|
227
|
+
* its whole value: with it, "no direct OS path call outside this package" is
|
|
228
|
+
* a greppable invariant instead of a rule with exceptions.
|
|
229
|
+
*
|
|
230
|
+
* Not {@link Tier.runtime}. Runtime holds state that does outlive the call
|
|
231
|
+
* and describes a live process — a pidfile, a socket, a lock — and the OS
|
|
232
|
+
* sweeps temp while such a process is still running.
|
|
233
|
+
*/
|
|
234
|
+
scratch() {
|
|
235
|
+
return new Dir(this.bases.scratch, []);
|
|
236
|
+
}
|
|
237
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,67 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@osdlc/tiered-dirs",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Machine-local directories for a vendor's products, on two axes: tier (vendor, suite, standalone tool) and home (durable, config, cache, state, logs, runtime), resolved over the per-OS directory table with native or XDG paths, plus an untiered scratch root. Override variables under a caller-chosen prefix, injectable roots, and tests kept off the real home. Twin of the Rust `tiered-dirs` crate.",
|
|
5
|
+
"license": "MIT OR Apache-2.0",
|
|
6
|
+
"author": "sksizer",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/osdlc/osdlc.git",
|
|
10
|
+
"directory": "packages/ts/tiered-dirs"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/osdlc/osdlc/tree/main/packages/ts/tiered-dirs#readme",
|
|
13
|
+
"bugs": "https://github.com/osdlc/osdlc/issues",
|
|
14
|
+
"keywords": [
|
|
15
|
+
"paths",
|
|
16
|
+
"xdg",
|
|
17
|
+
"app-dirs",
|
|
18
|
+
"config",
|
|
19
|
+
"cache",
|
|
20
|
+
"cross-platform",
|
|
21
|
+
"typescript"
|
|
22
|
+
],
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"type": "module",
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=22"
|
|
30
|
+
},
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"default": "./dist/index.js"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist/",
|
|
39
|
+
"README.md",
|
|
40
|
+
"CHANGELOG.md",
|
|
41
|
+
"LICENSE-MIT",
|
|
42
|
+
"LICENSE-APACHE",
|
|
43
|
+
"SECURITY.md",
|
|
44
|
+
"CONTRIBUTING.md"
|
|
45
|
+
],
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/node": "^26.1.0",
|
|
48
|
+
"@vitest/coverage-v8": "^5.0.5",
|
|
49
|
+
"oxlint": "^1.65.0",
|
|
50
|
+
"prettier": "3.9.6",
|
|
51
|
+
"typescript": "~6.0.3",
|
|
52
|
+
"vitest": "^5.0.5",
|
|
53
|
+
"@osdlc/prettier-config": "^0.2.3",
|
|
54
|
+
"@osdlc/tsconfig": "^0.3.3"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "tsc -p tsconfig.build.json",
|
|
58
|
+
"clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
59
|
+
"test": "vitest run",
|
|
60
|
+
"coverage": "vitest run --coverage",
|
|
61
|
+
"smoke": "node tests/smoke.mjs",
|
|
62
|
+
"format": "prettier --write .",
|
|
63
|
+
"format:check": "prettier --check .",
|
|
64
|
+
"lint": "oxlint .",
|
|
65
|
+
"typecheck": "tsc --noEmit && tsc -p tsconfig.build.json --noEmit"
|
|
66
|
+
}
|
|
6
67
|
}
|