@abseed/spectra-core 0.1.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/LICENSE +202 -0
- package/NOTICE +6 -0
- package/dist/answer.d.ts +40 -0
- package/dist/answer.js +72 -0
- package/dist/backlinks.d.ts +35 -0
- package/dist/backlinks.js +66 -0
- package/dist/changeset.d.ts +25 -0
- package/dist/changeset.js +157 -0
- package/dist/commit.d.ts +42 -0
- package/dist/commit.js +96 -0
- package/dist/conflicts.d.ts +29 -0
- package/dist/conflicts.js +64 -0
- package/dist/coverage.d.ts +83 -0
- package/dist/coverage.js +148 -0
- package/dist/expectationCheck.d.ts +43 -0
- package/dist/expectationCheck.js +86 -0
- package/dist/expectations.d.ts +66 -0
- package/dist/expectations.js +114 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +15 -0
- package/dist/propose.d.ts +19 -0
- package/dist/propose.js +32 -0
- package/dist/raise.d.ts +26 -0
- package/dist/raise.js +42 -0
- package/dist/schema.d.ts +1284 -0
- package/dist/schema.js +222 -0
- package/dist/specStore.d.ts +156 -0
- package/dist/specStore.js +1 -0
- package/dist/transcriptStore.d.ts +83 -0
- package/dist/transcriptStore.js +1 -0
- package/dist/types.d.ts +299 -0
- package/dist/types.js +11 -0
- package/dist/valueType.d.ts +23 -0
- package/dist/valueType.js +41 -0
- package/package.json +35 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The glossary schema. These types are the contract between the human authoring
|
|
3
|
+
* specs in the UI and the AI agent that implements them — everything else in this
|
|
4
|
+
* repo is a view over data shaped like this.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Who made a write. The kind distinguishes a person from the two agents; it is stamped
|
|
8
|
+
* server-side from the route or the agent definition, never taken from the client, so an actor
|
|
9
|
+
* in the sandbox cannot claim to be a human. `user` is the human account — absent until auth
|
|
10
|
+
* exists, and for an agent action it is eventually the human whose session it was.
|
|
11
|
+
*
|
|
12
|
+
* This is identity, kept separate from the `raisedBy` origin (which records *why* and in what
|
|
13
|
+
* pass). Optional on the records below: absent means the record predates identity tracking,
|
|
14
|
+
* the same way `appliedAt` is absent on changesets applied before it was recorded.
|
|
15
|
+
*/
|
|
16
|
+
export type AuthorKind = 'human' | 'spec' | 'coder';
|
|
17
|
+
export interface Author {
|
|
18
|
+
kind: AuthorKind;
|
|
19
|
+
user?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Whether a human-authored artifact is still being drafted or has been published to the team.
|
|
23
|
+
*
|
|
24
|
+
* `ready` is the published state — it counts as coverage, rides in the versioned contract, and
|
|
25
|
+
* the agents see it. `draft` is author-only and part of nothing until it is published, the same
|
|
26
|
+
* way a draft PR is not yet a request for review. Optional on the records below: absent means
|
|
27
|
+
* `ready`, so the specs already on disk (all published) need no rewrite. Agents always raise
|
|
28
|
+
* `ready`; drafting is a human convenience.
|
|
29
|
+
*/
|
|
30
|
+
export type RecordStatus = 'draft' | 'ready';
|
|
31
|
+
/**
|
|
32
|
+
* Who this glossary is, in one place. `name` is the display title (the UI header) and what the
|
|
33
|
+
* agents are told they are working on; `domain` is a one-line description of the subject the
|
|
34
|
+
* glossary describes — the framing an agent reads before it has read a single term.
|
|
35
|
+
*
|
|
36
|
+
* This is glossary *content*, not deployment config: it describes the project itself, so it is
|
|
37
|
+
* retrieved through the SpecStore (a file for the filesystem backend, a row for SQL, per-tenant
|
|
38
|
+
* once hosted) rather than baked into a prompt string. The *link* to a hosted project — endpoint,
|
|
39
|
+
* project id, credentials — is a separate, bootstrap concern and does not live here.
|
|
40
|
+
*/
|
|
41
|
+
export interface ProjectInfo {
|
|
42
|
+
name: string;
|
|
43
|
+
domain: string;
|
|
44
|
+
}
|
|
45
|
+
export type TermType = 'entity' | 'event' | 'function' | 'attribute-type';
|
|
46
|
+
export declare const TERM_TYPES: readonly TermType[];
|
|
47
|
+
export interface Attribute {
|
|
48
|
+
name: string;
|
|
49
|
+
/** A primitive (`string`, `number`, `boolean`, `date`) or `ref:<TermName>`, either with an optional `[]` suffix. */
|
|
50
|
+
valueType: string;
|
|
51
|
+
default?: unknown;
|
|
52
|
+
optional?: boolean;
|
|
53
|
+
}
|
|
54
|
+
export interface Term {
|
|
55
|
+
name: string;
|
|
56
|
+
type: TermType;
|
|
57
|
+
/** Short natural-language description — spec-worthy, precise enough to generate tests from. */
|
|
58
|
+
spec: string;
|
|
59
|
+
/** Single supertype (is-a), or null. */
|
|
60
|
+
parent: string | null;
|
|
61
|
+
/** Free cross-cutting labels; not a hierarchy. */
|
|
62
|
+
tags: string[];
|
|
63
|
+
attributes: Attribute[];
|
|
64
|
+
}
|
|
65
|
+
/** How the AI (or a hand-authored fixture) proposes an edit: structured ops, never free text. */
|
|
66
|
+
export type Op = {
|
|
67
|
+
op: 'add_entity';
|
|
68
|
+
term: string;
|
|
69
|
+
/** Defaults to `entity`; carries the other kinds so one op can add functions and events too. */
|
|
70
|
+
termType?: TermType;
|
|
71
|
+
parent?: string | null;
|
|
72
|
+
spec: string;
|
|
73
|
+
tags?: string[];
|
|
74
|
+
attributes?: Attribute[];
|
|
75
|
+
} | {
|
|
76
|
+
op: 'remove_entity';
|
|
77
|
+
term: string;
|
|
78
|
+
} | {
|
|
79
|
+
op: 'add_attribute';
|
|
80
|
+
term: string;
|
|
81
|
+
attribute: Attribute;
|
|
82
|
+
} | {
|
|
83
|
+
op: 'remove_attribute';
|
|
84
|
+
term: string;
|
|
85
|
+
attribute: string;
|
|
86
|
+
} | {
|
|
87
|
+
op: 'modify_spec';
|
|
88
|
+
term: string;
|
|
89
|
+
spec: string;
|
|
90
|
+
};
|
|
91
|
+
export type OpKind = Op['op'];
|
|
92
|
+
export interface Changeset {
|
|
93
|
+
id: string;
|
|
94
|
+
summary: string;
|
|
95
|
+
ops: Op[];
|
|
96
|
+
/** Plain-language test intentions; a changeset carrying passing tests is safer to auto-approve. */
|
|
97
|
+
tests: string[];
|
|
98
|
+
/** Who proposed it. Absent on changesets minted before identity was tracked. */
|
|
99
|
+
author?: Author;
|
|
100
|
+
/** Set when this changeset was minted by answering a Question — the id of that question. */
|
|
101
|
+
fromQuestion?: string;
|
|
102
|
+
/** ISO timestamp, written when the changeset lands. Absent while it is still pending. */
|
|
103
|
+
appliedAt?: string;
|
|
104
|
+
/**
|
|
105
|
+
* When code was written for this change. `null` means applied but not yet implemented —
|
|
106
|
+
* the state the `implements:` markers cannot detect, because a rewritten spec leaves
|
|
107
|
+
* every marker looking correct. Absent on changesets applied before this was tracked.
|
|
108
|
+
*/
|
|
109
|
+
implementedAt?: string | null;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* A question raised against the glossary — normally by an agent that tried to implement
|
|
113
|
+
* it and hit something the specs do not settle.
|
|
114
|
+
*
|
|
115
|
+
* The unit here is deliberately a *question*, not a "finding": if an entry cannot be
|
|
116
|
+
* phrased as something the human answers, it does not belong in the queue. That rules out
|
|
117
|
+
* the observations an implementation pass would otherwise flood it with, and it keeps the
|
|
118
|
+
* agent from quietly making product decisions by dressing a guess up as a proposal.
|
|
119
|
+
*/
|
|
120
|
+
export interface Question {
|
|
121
|
+
id: string;
|
|
122
|
+
/** The question itself, answerable as written. */
|
|
123
|
+
asks: string;
|
|
124
|
+
/** Why it is being asked — must quote the spec text in conflict, not just describe inconvenience. */
|
|
125
|
+
because: string;
|
|
126
|
+
raisedBy: QuestionOrigin;
|
|
127
|
+
/** Who raised it. Absent on questions raised before identity was tracked. */
|
|
128
|
+
author?: Author;
|
|
129
|
+
/** Draft or published. Absent means `ready`. Agent-raised questions are always `ready`. */
|
|
130
|
+
status?: RecordStatus;
|
|
131
|
+
/**
|
|
132
|
+
* Bumped by the store on every write. A caller that read revision N can send it back as the
|
|
133
|
+
* expected revision on its next write; the store refuses if the record has moved since. Absent
|
|
134
|
+
* means 1 — the birth value and what a record predating this reads as.
|
|
135
|
+
*/
|
|
136
|
+
rev?: number;
|
|
137
|
+
/**
|
|
138
|
+
* Candidate answers. The count is the answer shape, so there is no separate field to
|
|
139
|
+
* keep in sync: one option is approve-or-decline, several is a choice, none means only
|
|
140
|
+
* the human can write the spec text.
|
|
141
|
+
*/
|
|
142
|
+
options: QuestionOption[];
|
|
143
|
+
/** Null while open. Kept after answering — the reasoning outlives the changeset. */
|
|
144
|
+
answer: Answer | null;
|
|
145
|
+
}
|
|
146
|
+
export interface QuestionOrigin {
|
|
147
|
+
/** What was being done when it came up, e.g. `implementation`. */
|
|
148
|
+
pass: string;
|
|
149
|
+
file?: string;
|
|
150
|
+
/** Terms the question is about, so it can be shown against them in the glossary. */
|
|
151
|
+
terms: string[];
|
|
152
|
+
}
|
|
153
|
+
export interface QuestionOption {
|
|
154
|
+
label: string;
|
|
155
|
+
/** The tradeoff in plain language, including what answering this way costs. */
|
|
156
|
+
detail?: string;
|
|
157
|
+
/** What would go into the pending queue. Null when this option changes no specs. */
|
|
158
|
+
proposal: Proposal | null;
|
|
159
|
+
}
|
|
160
|
+
/** A changeset body without an id — the id is minted when the option is chosen. */
|
|
161
|
+
export interface Proposal {
|
|
162
|
+
summary: string;
|
|
163
|
+
ops: Op[];
|
|
164
|
+
tests: string[];
|
|
165
|
+
}
|
|
166
|
+
export interface Answer {
|
|
167
|
+
/** Label of the chosen option, or null when the human answered without taking one. */
|
|
168
|
+
chose: string | null;
|
|
169
|
+
note: string;
|
|
170
|
+
/** ISO timestamp. */
|
|
171
|
+
answeredAt: string;
|
|
172
|
+
/** Id of the changeset this answer put into the pending queue, if any. */
|
|
173
|
+
changesetId?: string;
|
|
174
|
+
/** Who answered. Absent on answers recorded before identity was tracked. */
|
|
175
|
+
author?: Author;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* What someone should be able to expect, stated outside the prose.
|
|
179
|
+
*
|
|
180
|
+
* A Term says what something *is*; an Expectation says what *happens* in a specific case.
|
|
181
|
+
* Both are normative, and keeping them apart buys three things the prose cannot:
|
|
182
|
+
*
|
|
183
|
+
* - Expectations accumulate. Using the app turns up scenarios nobody thought of the first
|
|
184
|
+
* time, and adding one must not mean rewriting a definition that was already correct.
|
|
185
|
+
* - They are addressable. An id is what lets a test name cite one, a QA agent resolve one,
|
|
186
|
+
* and a review notice two that say the same thing.
|
|
187
|
+
* - They are countable against the vocabulary, which is what makes coverage computable —
|
|
188
|
+
* see `coverage.ts`.
|
|
189
|
+
*
|
|
190
|
+
* Deliberately *not* a Term. A Term is roughly a class, a thing the product has; an
|
|
191
|
+
* Expectation is a statement about the vocabulary rather than part of it. Making it a
|
|
192
|
+
* `TermType` would hand it a parent, attributes, a slot in the glossary browser beside Task,
|
|
193
|
+
* and a demand for an `implements:` marker — none of which mean anything here. It sits in the
|
|
194
|
+
* same tier as Question and Changeset: first-class, references terms, is not one.
|
|
195
|
+
*/
|
|
196
|
+
export type ExpectationKind = 'functional' | 'non-functional';
|
|
197
|
+
export type ClashKind =
|
|
198
|
+
/** Names a term the glossary does not have. */
|
|
199
|
+
'unknown-term'
|
|
200
|
+
/** Says what a live expectation already says. */
|
|
201
|
+
| 'duplicate'
|
|
202
|
+
/** Concerns exactly the same terms as a live one, in different words. */
|
|
203
|
+
| 'overlaps'
|
|
204
|
+
/** Cannot hold at the same time as a term's spec. */
|
|
205
|
+
| 'contradicts'
|
|
206
|
+
/** Restates a spec without adding a scenario. */
|
|
207
|
+
| 'restates';
|
|
208
|
+
/** Something a draft clashed with, quoted rather than described so both can be read together. */
|
|
209
|
+
export interface Clash {
|
|
210
|
+
kind: ClashKind;
|
|
211
|
+
/** The term name or expectation id this is about. */
|
|
212
|
+
subject: string;
|
|
213
|
+
detail: string;
|
|
214
|
+
quote?: string;
|
|
215
|
+
}
|
|
216
|
+
export interface Expectation {
|
|
217
|
+
id: string;
|
|
218
|
+
/**
|
|
219
|
+
* Which verifier this is for, and that is the whole reason the field exists rather than
|
|
220
|
+
* taxonomy for its own sake. A functional expectation is phrased in glossary vocabulary and
|
|
221
|
+
* becomes a unit test over the domain. A non-functional one — latency, accessibility,
|
|
222
|
+
* persistence — is a property of an implementation, which this glossary deliberately does
|
|
223
|
+
* not constrain, so it is exempt from the vocabulary rule and is checked by driving a
|
|
224
|
+
* running build instead.
|
|
225
|
+
*
|
|
226
|
+
* It is stored rather than derived: emptiness of `terms` looked like it would encode this,
|
|
227
|
+
* but "listing Tasks in a Project holding ten thousand Tasks stays responsive" is
|
|
228
|
+
* non-functional and names two terms.
|
|
229
|
+
*/
|
|
230
|
+
kind: ExpectationKind;
|
|
231
|
+
/** Who raised it. Absent on expectations raised before identity was tracked. */
|
|
232
|
+
author?: Author;
|
|
233
|
+
/**
|
|
234
|
+
* Draft or published. Absent means `ready`. A draft counts toward nothing — not coverage,
|
|
235
|
+
* not the versioned contract, and the agents do not see it — until it is published.
|
|
236
|
+
*/
|
|
237
|
+
status?: RecordStatus;
|
|
238
|
+
/** Store revision, bumped on every write; the basis for optimistic concurrency. Absent means 1. */
|
|
239
|
+
rev?: number;
|
|
240
|
+
/** Glossary terms this concerns. May be empty for a non-functional expectation that scopes to the whole app. */
|
|
241
|
+
terms: string[];
|
|
242
|
+
/** The situation. Empty when the expectation is unconditional. */
|
|
243
|
+
given: string;
|
|
244
|
+
/** What must hold. For a functional expectation, phrased using only glossary vocabulary. */
|
|
245
|
+
expect: string;
|
|
246
|
+
raisedBy: ExpectationOrigin;
|
|
247
|
+
/**
|
|
248
|
+
* Id of the expectation that replaced this one, or null while it is live.
|
|
249
|
+
*
|
|
250
|
+
* Expectations move — a decision changes, or the first phrasing was imprecise — and an
|
|
251
|
+
* edit in place would lose both the old wording and the reason. So they are superseded
|
|
252
|
+
* rather than rewritten, the same way an answer stays in its question file and a changeset
|
|
253
|
+
* moves to `applied/` instead of vanishing. Three things fall out: a test named `e-014`
|
|
254
|
+
* still resolves years later, the reasoning is not re-derived, and the one dangerous
|
|
255
|
+
* direction — quietly weakening an expectation to turn a red check green — leaves an
|
|
256
|
+
* artifact in the history that is already reviewed.
|
|
257
|
+
*/
|
|
258
|
+
supersededBy: string | null;
|
|
259
|
+
/**
|
|
260
|
+
* Why it stopped applying. Written when it is retired, absent while it is live — the one
|
|
261
|
+
* field that only ever appears on a retired copy, because a live expectation has no such
|
|
262
|
+
* reason to record.
|
|
263
|
+
*/
|
|
264
|
+
retiredBecause?: string;
|
|
265
|
+
/**
|
|
266
|
+
* What this clashed with, and was written down anyway.
|
|
267
|
+
*
|
|
268
|
+
* The check that finds these does not refuse the write, deliberately: a draft that
|
|
269
|
+
* contradicts a spec is often a legitimate thing to want that the glossary does not allow
|
|
270
|
+
* yet, and a model deciding what you may expect from your own product is the wrong
|
|
271
|
+
* authority. But a finding that lives only in the browser for the second before you click
|
|
272
|
+
* is worse than no finding — the expectation then lands looking exactly like one that came
|
|
273
|
+
* back clean, and nothing downstream can tell the difference.
|
|
274
|
+
*
|
|
275
|
+
* So the disagreement travels with the statement. An implementer reading this knows not to
|
|
276
|
+
* go and make it true, because the glossary currently says otherwise and only a human can
|
|
277
|
+
* settle which gives. Empty means it was checked and clean, or predates the check.
|
|
278
|
+
*/
|
|
279
|
+
contested: Clash[];
|
|
280
|
+
}
|
|
281
|
+
export interface ExpectationOrigin {
|
|
282
|
+
/** What was being done when it came up, e.g. `implementation`, `usage`, `review`. */
|
|
283
|
+
pass: string;
|
|
284
|
+
/** Question or changeset it follows from, if any. */
|
|
285
|
+
from?: string;
|
|
286
|
+
file?: string;
|
|
287
|
+
}
|
|
288
|
+
export type Severity = 'error' | 'warning';
|
|
289
|
+
export interface Diagnostic {
|
|
290
|
+
/** Index into the op list this diagnostic belongs to, or null when it describes the resulting state as a whole. */
|
|
291
|
+
opIndex: number | null;
|
|
292
|
+
severity: Severity;
|
|
293
|
+
message: string;
|
|
294
|
+
}
|
|
295
|
+
/** A term file that could not be read or did not match the schema. Surfaced in the UI instead of crashing. */
|
|
296
|
+
export interface SourceProblem {
|
|
297
|
+
file: string;
|
|
298
|
+
message: string;
|
|
299
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The glossary schema. These types are the contract between the human authoring
|
|
3
|
+
* specs in the UI and the AI agent that implements them — everything else in this
|
|
4
|
+
* repo is a view over data shaped like this.
|
|
5
|
+
*/
|
|
6
|
+
export const TERM_TYPES = [
|
|
7
|
+
'entity',
|
|
8
|
+
'event',
|
|
9
|
+
'function',
|
|
10
|
+
'attribute-type',
|
|
11
|
+
];
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attribute value types are a tiny string grammar rather than a nested object, so
|
|
3
|
+
* the JSON stays hand-editable:
|
|
4
|
+
*
|
|
5
|
+
* string | number | boolean | date | ref:<TermName> (each with an optional `[]` suffix)
|
|
6
|
+
*/
|
|
7
|
+
export declare const PRIMITIVES: readonly ["string", "number", "boolean", "date"];
|
|
8
|
+
export type Primitive = (typeof PRIMITIVES)[number];
|
|
9
|
+
export type ParsedValueType = {
|
|
10
|
+
kind: 'primitive';
|
|
11
|
+
name: Primitive;
|
|
12
|
+
array: boolean;
|
|
13
|
+
} | {
|
|
14
|
+
kind: 'ref';
|
|
15
|
+
name: string;
|
|
16
|
+
array: boolean;
|
|
17
|
+
};
|
|
18
|
+
export declare function parseValueType(raw: string): ParsedValueType | null;
|
|
19
|
+
export declare function isValueType(raw: string): boolean;
|
|
20
|
+
/** The referenced Term name, or null when the value type is a primitive or unparseable. */
|
|
21
|
+
export declare function refName(raw: string): string | null;
|
|
22
|
+
export declare function formatValueType(parsed: ParsedValueType): string;
|
|
23
|
+
export declare function describeValueTypeError(raw: string): string;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attribute value types are a tiny string grammar rather than a nested object, so
|
|
3
|
+
* the JSON stays hand-editable:
|
|
4
|
+
*
|
|
5
|
+
* string | number | boolean | date | ref:<TermName> (each with an optional `[]` suffix)
|
|
6
|
+
*/
|
|
7
|
+
export const PRIMITIVES = ['string', 'number', 'boolean', 'date'];
|
|
8
|
+
const VALUE_TYPE = /^(ref:)?([A-Za-z_][A-Za-z0-9_]*)(\[\])?$/;
|
|
9
|
+
export function parseValueType(raw) {
|
|
10
|
+
const match = VALUE_TYPE.exec(raw.trim());
|
|
11
|
+
if (!match)
|
|
12
|
+
return null;
|
|
13
|
+
const [, refPrefix, name, arraySuffix] = match;
|
|
14
|
+
const array = arraySuffix === '[]';
|
|
15
|
+
if (refPrefix)
|
|
16
|
+
return { kind: 'ref', name: name, array };
|
|
17
|
+
if (PRIMITIVES.includes(name)) {
|
|
18
|
+
return { kind: 'primitive', name: name, array };
|
|
19
|
+
}
|
|
20
|
+
// A bare capitalised word is almost certainly a Term reference missing its prefix.
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
export function isValueType(raw) {
|
|
24
|
+
return parseValueType(raw) !== null;
|
|
25
|
+
}
|
|
26
|
+
/** The referenced Term name, or null when the value type is a primitive or unparseable. */
|
|
27
|
+
export function refName(raw) {
|
|
28
|
+
const parsed = parseValueType(raw);
|
|
29
|
+
return parsed?.kind === 'ref' ? parsed.name : null;
|
|
30
|
+
}
|
|
31
|
+
export function formatValueType(parsed) {
|
|
32
|
+
const prefix = parsed.kind === 'ref' ? 'ref:' : '';
|
|
33
|
+
return `${prefix}${parsed.name}${parsed.array ? '[]' : ''}`;
|
|
34
|
+
}
|
|
35
|
+
export function describeValueTypeError(raw) {
|
|
36
|
+
const bare = raw.trim().replace(/\[\]$/, '');
|
|
37
|
+
if (/^[A-Z]/.test(bare)) {
|
|
38
|
+
return `"${raw}" looks like a Term reference — did you mean "ref:${raw}"?`;
|
|
39
|
+
}
|
|
40
|
+
return `"${raw}" is not a valid value type (expected ${PRIMITIVES.join(', ')} or ref:<TermName>, with an optional [] suffix)`;
|
|
41
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@abseed/spectra-core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The Spectra engine: glossary types, the changeset grammar, and the storage seams.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"author": "Luke Pezet",
|
|
7
|
+
"homepage": "https://github.com/lpezet/spectra/tree/main/packages/core",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/lpezet/spectra.git",
|
|
11
|
+
"directory": "packages/core"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"spectra",
|
|
15
|
+
"spec-driven",
|
|
16
|
+
"glossary",
|
|
17
|
+
"changeset"
|
|
18
|
+
],
|
|
19
|
+
"type": "module",
|
|
20
|
+
"main": "./dist/index.js",
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"default": "./dist/index.js"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"zod": "^3.24.1"
|
|
31
|
+
},
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
}
|
|
35
|
+
}
|