primitive-admin 1.1.0-alpha.64 → 1.1.0-alpha.66
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/README.md +4 -4
- package/assets/skill/skills/primitive-platform/SKILL.md +83 -13
- package/dist/bin/primitive.js +6 -3
- package/dist/bin/primitive.js.map +1 -1
- package/dist/src/commands/apps.js +14 -5
- package/dist/src/commands/apps.js.map +1 -1
- package/dist/src/commands/config.js +11 -4
- package/dist/src/commands/config.js.map +1 -1
- package/dist/src/commands/cron-triggers.js +25 -28
- package/dist/src/commands/cron-triggers.js.map +1 -1
- package/dist/src/commands/env.d.ts +12 -1
- package/dist/src/commands/env.js +96 -21
- package/dist/src/commands/env.js.map +1 -1
- package/dist/src/commands/feature-flags.d.ts +14 -0
- package/dist/src/commands/feature-flags.js +116 -0
- package/dist/src/commands/feature-flags.js.map +1 -0
- package/dist/src/commands/init.d.ts +1 -1
- package/dist/src/commands/init.js +78 -25
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/integrations.js +55 -2
- package/dist/src/commands/integrations.js.map +1 -1
- package/dist/src/commands/prompts.js +58 -3
- package/dist/src/commands/prompts.js.map +1 -1
- package/dist/src/commands/rule-sets.d.ts +1 -1
- package/dist/src/commands/rule-sets.js +1 -0
- package/dist/src/commands/rule-sets.js.map +1 -1
- package/dist/src/commands/sync-app-settings.d.ts +83 -55
- package/dist/src/commands/sync-app-settings.js +131 -200
- package/dist/src/commands/sync-app-settings.js.map +1 -1
- package/dist/src/commands/sync.d.ts +309 -40
- package/dist/src/commands/sync.js +2493 -397
- package/dist/src/commands/sync.js.map +1 -1
- package/dist/src/commands/users.js +92 -1
- package/dist/src/commands/users.js.map +1 -1
- package/dist/src/commands/webhooks.js +59 -2
- package/dist/src/commands/webhooks.js.map +1 -1
- package/dist/src/commands/workflows.js +32 -22
- package/dist/src/commands/workflows.js.map +1 -1
- package/dist/src/lib/api-client.d.ts +51 -4
- package/dist/src/lib/api-client.js +71 -7
- package/dist/src/lib/api-client.js.map +1 -1
- package/dist/src/lib/app-settings-descriptor.d.ts +143 -15
- package/dist/src/lib/app-settings-descriptor.js +315 -28
- package/dist/src/lib/app-settings-descriptor.js.map +1 -1
- package/dist/src/lib/config-object-descriptor.js +6 -14
- package/dist/src/lib/config-object-descriptor.js.map +1 -1
- package/dist/src/lib/config-payload.js +14 -3
- package/dist/src/lib/config-payload.js.map +1 -1
- package/dist/src/lib/config-surface.d.ts +38 -1
- package/dist/src/lib/config-surface.js +123 -1
- package/dist/src/lib/config-surface.js.map +1 -1
- package/dist/src/lib/env-resolver-core.d.ts +147 -0
- package/dist/src/lib/env-resolver-core.js +265 -0
- package/dist/src/lib/env-resolver-core.js.map +1 -0
- package/dist/src/lib/env-resolver.d.ts +28 -6
- package/dist/src/lib/env-resolver.js +44 -32
- package/dist/src/lib/env-resolver.js.map +1 -1
- package/dist/src/lib/generated-config-surfaces.d.ts +66 -0
- package/dist/src/lib/generated-config-surfaces.js +391 -104
- package/dist/src/lib/generated-config-surfaces.js.map +1 -1
- package/dist/src/lib/integration-request-config.d.ts +30 -0
- package/dist/src/lib/integration-request-config.js +145 -0
- package/dist/src/lib/integration-request-config.js.map +1 -0
- package/dist/src/lib/local-state.d.ts +55 -0
- package/dist/src/lib/local-state.js +167 -0
- package/dist/src/lib/local-state.js.map +1 -0
- package/dist/src/lib/log-inspection.d.ts +2 -2
- package/dist/src/lib/log-inspection.js +3 -3
- package/dist/src/lib/log-inspection.js.map +1 -1
- package/dist/src/lib/object-status-filter.d.ts +22 -0
- package/dist/src/lib/object-status-filter.js +45 -0
- package/dist/src/lib/object-status-filter.js.map +1 -0
- package/dist/src/lib/server-text-normalization.d.ts +51 -0
- package/dist/src/lib/server-text-normalization.js +90 -0
- package/dist/src/lib/server-text-normalization.js.map +1 -0
- package/dist/src/lib/swift-codegen/generator.js +2 -4
- package/dist/src/lib/swift-codegen/generator.js.map +1 -1
- package/dist/src/lib/sync-resource-types.d.ts +8 -0
- package/dist/src/lib/sync-resource-types.js +21 -2
- package/dist/src/lib/sync-resource-types.js.map +1 -1
- package/dist/src/lib/template.d.ts +27 -0
- package/dist/src/lib/template.js +46 -20
- package/dist/src/lib/template.js.map +1 -1
- package/dist/src/lib/workflow-defaults.d.ts +1 -2
- package/dist/src/lib/workflow-defaults.js +3 -3
- package/dist/src/lib/workflow-defaults.js.map +1 -1
- package/dist/src/lib/workflow-payload.js +0 -6
- package/dist/src/lib/workflow-payload.js.map +1 -1
- package/dist/src/types/index.d.ts +32 -7
- package/package.json +2 -2
|
@@ -84,15 +84,15 @@ export declare function resolveActiveWorkflowConfig(workflow: any, configs: any[
|
|
|
84
84
|
* server's accepted-value canonicalization
|
|
85
85
|
* (`src/workflows/config/workflow-field-handlers.ts`). A value the server
|
|
86
86
|
* would REJECT is left alone: push fails loudly for those, so there is
|
|
87
|
-
* nothing to preview. #2743 — without this, `
|
|
87
|
+
* nothing to preview. #2743 — without this, `dequeueOrder = "LIFO"` or
|
|
88
88
|
* `perUserMaxRunning = 4.9` pushes fine and then reports Modified forever.
|
|
89
89
|
* 4. Fill the model defaults (`WORKFLOW_MODEL_DEFAULTS`) for what the file
|
|
90
90
|
* omits. `perAppMax*` / `queueTtlSeconds` joined that list in #1177: once
|
|
91
91
|
* the pull serializer emitted them (a GET always returns them, they carry
|
|
92
92
|
* a non-null model default), an unchanged workflow hashed unequal to a
|
|
93
93
|
* local TOML that omits them — a false `modified` that made `config pull`
|
|
94
|
-
* rewrite the file just to inject defaults. `status`
|
|
95
|
-
*
|
|
94
|
+
* rewrite the file just to inject defaults. (`status` was on this list
|
|
95
|
+
* until #2803 took availability off the config wire entirely.)
|
|
96
96
|
* 5. Fill `activeConfigName` with the name the server itself creates
|
|
97
97
|
* (`default`), so a hand-authored file that never named a config compares
|
|
98
98
|
* equal to the workflow that pushing it produces (#2743). This is the
|
|
@@ -482,23 +482,70 @@ export declare function workflowActiveConfigNameUnresolvable(configDir: string,
|
|
|
482
482
|
* repeats forever because no push changes it. But the repo does not describe
|
|
483
483
|
* which configuration is live, and only `config pull` records it, so the fact is
|
|
484
484
|
* carried as a hint on the Synced row — the same way an operational disable is
|
|
485
|
-
* (`
|
|
485
|
+
* (`outOfServiceHint`). The server-created `default` is the shape a
|
|
486
486
|
* hand-authored file already means, so it is silent.
|
|
487
487
|
*
|
|
488
488
|
* Pure and non-throwing, like every other diff predicate. Returns the existing
|
|
489
489
|
* hint untouched when there is nothing to add.
|
|
490
490
|
*/
|
|
491
491
|
export declare function unmanagedActiveConfigHint(localParsed: any, workflow: any, configs: any[], existingHint?: string): string | undefined;
|
|
492
|
+
/**
|
|
493
|
+
* Drop the archived rows from a pull listing (#2803).
|
|
494
|
+
*
|
|
495
|
+
* A tombstone is retired, not configuration. Exporting one wrote a file the
|
|
496
|
+
* repo then claimed to describe — and, once `status` left the TOML surface, a
|
|
497
|
+
* file whose very next push would fail on the `status` line it carried.
|
|
498
|
+
* `config push --prune` is the path that reclaims an archived row; pull's job
|
|
499
|
+
* is to describe what is configured.
|
|
500
|
+
*/
|
|
501
|
+
export declare function skipArchivedForPull<T extends {
|
|
502
|
+
status?: unknown;
|
|
503
|
+
}>(items: T[]): T[];
|
|
504
|
+
/**
|
|
505
|
+
* The sync-state entries a pull keeps for the archived rows it did NOT export
|
|
506
|
+
* (#2803).
|
|
507
|
+
*
|
|
508
|
+
* Pull removes a tombstone's file, and prune candidates come only from prior
|
|
509
|
+
* sync state — so dropping the state entry with the file would strand the row:
|
|
510
|
+
* no file to delete, no managed key to prune, and (for webhooks and cron
|
|
511
|
+
* triggers) a cap slot and a reserved key nothing could ever reclaim, which is
|
|
512
|
+
* the exact failure #2232 fixed. So the pull records the tombstone instead: no
|
|
513
|
+
* file, but a managed entry carrying the archived row's CURRENT `modifiedAt`,
|
|
514
|
+
* so the next opt-in, confirmed `config push --prune` point-reads it, finds it
|
|
515
|
+
* unchanged, and hard-deletes it — the reclamation path the model promises.
|
|
516
|
+
*
|
|
517
|
+
* Only keys the config tree already managed are recorded. A row archived
|
|
518
|
+
* server-side that this repo never described is not this repo's to delete.
|
|
519
|
+
*
|
|
520
|
+
* Pure, so it is unit-testable without a live server.
|
|
521
|
+
*/
|
|
522
|
+
export declare function archivedTombstoneEntries(items: any[], select: {
|
|
523
|
+
keyOf: (item: any) => string;
|
|
524
|
+
idOf: (item: any) => string;
|
|
525
|
+
}, managedKeys: Iterable<string>): Record<string, {
|
|
526
|
+
id: string;
|
|
527
|
+
modifiedAt: string;
|
|
528
|
+
}>;
|
|
492
529
|
/**
|
|
493
530
|
* The `config diff` annotation for an object an operator took out of service
|
|
494
|
-
* (issue #2645, criterion 9).
|
|
531
|
+
* (issue #2645, criterion 9; #2803).
|
|
495
532
|
*
|
|
496
|
-
*
|
|
497
|
-
* is NOT drift — the committed configuration still describes it
|
|
498
|
-
* so out loud is the point: the reason the
|
|
499
|
-
* write used to make `config diff` report a difference
|
|
500
|
-
* created (#1976). Reporting it as a hint on an
|
|
501
|
-
* keeps that promise visible instead of merely
|
|
533
|
+
* Availability is deliberately not a TOML key, so an object that is out of
|
|
534
|
+
* service is NOT drift — the committed configuration still describes it
|
|
535
|
+
* exactly. Saying so out loud is the point: the reason the control exists is
|
|
536
|
+
* that an out-of-band write used to make `config diff` report a difference
|
|
537
|
+
* nobody intentionally created (#1976). Reporting it as a hint on an
|
|
538
|
+
* otherwise-`Synced` row is what keeps that promise visible instead of merely
|
|
539
|
+
* true.
|
|
540
|
+
*
|
|
541
|
+
* Reads either spelling: the canonical `status: "inactive"` for a type that has
|
|
542
|
+
* cut over to #2803's single control, and the superseded
|
|
543
|
+
* `operationallyDisabled` boolean for one that has not yet.
|
|
544
|
+
*
|
|
545
|
+
* An ARCHIVED row gets its own annotation. `config pull` no longer exports
|
|
546
|
+
* tombstones, so without one an archived webhook or integration whose file is
|
|
547
|
+
* still in the tree read as plain `Synced` — the one status where "the server
|
|
548
|
+
* has exactly what the file says" is most misleading.
|
|
502
549
|
*
|
|
503
550
|
* When the row already carries a hint — a real content difference — the toggle
|
|
504
551
|
* is APPENDED rather than substituted: the drift is the part that needs acting
|
|
@@ -506,7 +553,7 @@ export declare function unmanagedActiveConfigHint(localParsed: any, workflow: an
|
|
|
506
553
|
*
|
|
507
554
|
* Pure, so it is unit-testable without a live server.
|
|
508
555
|
*/
|
|
509
|
-
export declare function
|
|
556
|
+
export declare function outOfServiceHint(remote: any, existingHint?: string): string | undefined;
|
|
510
557
|
/**
|
|
511
558
|
* Serialize an integration DETAIL response to its `integrations/<key>.toml`.
|
|
512
559
|
*
|
|
@@ -694,6 +741,16 @@ export declare function rewriteWorkflowSchemasToNative(rawToml: string, workflow
|
|
|
694
741
|
content: string;
|
|
695
742
|
changed: boolean;
|
|
696
743
|
};
|
|
744
|
+
/**
|
|
745
|
+
* #2644 — `config push` rejects any key the vendored definition does not
|
|
746
|
+
* recognize, so a mistyped or newer-server key fails locally instead of being
|
|
747
|
+
* silently dropped on the way to the server (design gate, 2026-08-12). Returns
|
|
748
|
+
* the error message, or null when every key is known.
|
|
749
|
+
*
|
|
750
|
+
* Works for any migrated type, single or repeated: a repeated table's entries
|
|
751
|
+
* are checked one by one and the unknown keys reported together, so a typo in
|
|
752
|
+
* the third `[[configs]]` block is as loud as one in the first.
|
|
753
|
+
*/
|
|
697
754
|
export declare function configUnknownKeyError(filePath: string, tomlData: any, table: ConfigTable): string | null;
|
|
698
755
|
/**
|
|
699
756
|
* #2644 — the document-level half of the same rejection: a top-level table the
|
|
@@ -727,6 +784,32 @@ export declare function unknownKeyPreflightTargets(): Array<{
|
|
|
727
784
|
surface: ConfigObjectSurface;
|
|
728
785
|
tables: ConfigTable[];
|
|
729
786
|
}>;
|
|
787
|
+
/** One config file the TOML preflight rejects, and the row it belongs to. */
|
|
788
|
+
export interface ConfigFileValidationError {
|
|
789
|
+
/** The `config diff` row type, e.g. `prompt`. */
|
|
790
|
+
type: string;
|
|
791
|
+
/** The row key the local file pairs on. */
|
|
792
|
+
key: string;
|
|
793
|
+
filePath: string;
|
|
794
|
+
/** The messages, verbatim — the same text `config push` aborts with. */
|
|
795
|
+
messages: string[];
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* Every per-entity config file whose TOML `config push` would refuse (#2880
|
|
799
|
+
* criterion 1, symmetry).
|
|
800
|
+
*
|
|
801
|
+
* One collector, two commands. Push aborts on the messages before issuing a
|
|
802
|
+
* request; `config diff` renders them as their own rows, because the failure
|
|
803
|
+
* this closes is a file diff calls Synced and push then rejects — the state the
|
|
804
|
+
* issue's reporter hit after #2803 retired `[prompt].status`, where a local key
|
|
805
|
+
* with no server counterpart is invisible to a comparison and fatal to a push.
|
|
806
|
+
*
|
|
807
|
+
* Three checks, all definition-driven: the document's table shape, keys no
|
|
808
|
+
* definition declares (retired keys among them), and — since #2880 — values
|
|
809
|
+
* whose spelling is not the declared type. A file that does not parse is left
|
|
810
|
+
* to the per-type loop that reads it, which names the parse error itself.
|
|
811
|
+
*/
|
|
812
|
+
export declare function collectConfigFileValidationErrors(configDir: string): ConfigFileValidationError[];
|
|
730
813
|
/**
|
|
731
814
|
* Every `<key>.tests/*.toml` sidecar under a config directory.
|
|
732
815
|
*
|
|
@@ -1051,12 +1134,47 @@ export interface ConfigDiffMaps {
|
|
|
1051
1134
|
export interface ConfigDiffSpec {
|
|
1052
1135
|
label: string;
|
|
1053
1136
|
table: ConfigTable;
|
|
1054
|
-
/**
|
|
1055
|
-
|
|
1137
|
+
/**
|
|
1138
|
+
* TOML doc -> the wire-shaped entity (the same parse `config push` uses).
|
|
1139
|
+
*
|
|
1140
|
+
* `extra` is the per-type context the projection needs and the document does
|
|
1141
|
+
* not carry (#2880): a test case's id→name lookups, through which the two
|
|
1142
|
+
* spellings of a reference meet. Mirrors `serialize`, which has taken one
|
|
1143
|
+
* since the database types joined.
|
|
1144
|
+
*/
|
|
1145
|
+
parse(doc: any, extra?: any): any;
|
|
1056
1146
|
/** Server entity -> the TOML `config pull` would write. */
|
|
1057
1147
|
serialize(record: any, maps: ConfigDiffMaps, extra?: any): string;
|
|
1058
1148
|
/** Structural values to hash beside the definition's fields. */
|
|
1059
|
-
extras?(entity: any, doc: any): Record<string, any>;
|
|
1149
|
+
extras?(entity: any, doc: any, extra?: any): Record<string, any>;
|
|
1150
|
+
/**
|
|
1151
|
+
* Rewrite the parsed entity the way the SERVER rewrites an accepted one
|
|
1152
|
+
* (#2880 DSO-003).
|
|
1153
|
+
*
|
|
1154
|
+
* Some handlers canonicalize on the way in — an integration's base URL gains
|
|
1155
|
+
* a trailing slash, its methods are upper-cased — so the state the server
|
|
1156
|
+
* returns is not the text the file holds. Comparing them raw makes an
|
|
1157
|
+
* authored-but-noncanonical value `local-edited` on every run and push
|
|
1158
|
+
* re-apply the same update forever; restamping the baseline cannot fix it,
|
|
1159
|
+
* because the local side never moves. Applied to both sides (the remote
|
|
1160
|
+
* reaches it through the same projection), so it must be idempotent.
|
|
1161
|
+
*/
|
|
1162
|
+
canonicalize?(entity: any): void;
|
|
1163
|
+
/**
|
|
1164
|
+
* The projected keys THIS FILE does not manage (#2880 criterion 3).
|
|
1165
|
+
*
|
|
1166
|
+
* A handful of fields are present-only by design: push sends them when the
|
|
1167
|
+
* file spells them and leaves the server's value alone when it does not,
|
|
1168
|
+
* because "absent" here means "not authored here" rather than "cleared" — a
|
|
1169
|
+
* webhook with no `[verification]` section at all, whose signing material a
|
|
1170
|
+
* push must not revoke as a side effect. Comparing such a field against a
|
|
1171
|
+
* server that holds one reports a difference push will never act on: the
|
|
1172
|
+
* update omits the key, the server keeps its value, and the row comes back
|
|
1173
|
+
* Modified on every run. So the local side inherits the remote value for
|
|
1174
|
+
* exactly the keys the file leaves unmanaged, and the two commands agree
|
|
1175
|
+
* that there is nothing to do.
|
|
1176
|
+
*/
|
|
1177
|
+
unmanaged?(doc: any): string[];
|
|
1060
1178
|
/** Whether the entity carries a rule-set reference needing resolution. */
|
|
1061
1179
|
ruleSetRef?: boolean;
|
|
1062
1180
|
/** How the type names itself in the rule-set resolution message. */
|
|
@@ -1064,9 +1182,10 @@ export interface ConfigDiffSpec {
|
|
|
1064
1182
|
}
|
|
1065
1183
|
/**
|
|
1066
1184
|
* Every type the shared comparator serves — the types `config diff` compares
|
|
1067
|
-
* field-for-field, and that `config push` gates on the same projection
|
|
1068
|
-
*
|
|
1069
|
-
* in #
|
|
1185
|
+
* field-for-field, and that `config push` gates on the same projection
|
|
1186
|
+
* (#2731 B1/B2; webhooks joined the comparator in #2757 and their push joined
|
|
1187
|
+
* the same gate in #2880, so no registered type answers "changed?" from the
|
|
1188
|
+
* file's bytes any more).
|
|
1070
1189
|
*
|
|
1071
1190
|
* Published so the round-trip acceptance bar (#2731 B9) can require a fixture
|
|
1072
1191
|
* per type instead of listing them a second time by hand: converting a type is
|
|
@@ -1081,11 +1200,41 @@ export declare function configDiffSpec(label: string): ConfigDiffSpec;
|
|
|
1081
1200
|
* a push that needs to SAY what differs and a diff that only needs to know THAT
|
|
1082
1201
|
* something differs read the same projection.
|
|
1083
1202
|
*/
|
|
1084
|
-
export declare function projectLocalConfig(spec: ConfigDiffSpec, parsedToml: any, maps: ConfigDiffMaps): Record<string, any>;
|
|
1203
|
+
export declare function projectLocalConfig(spec: ConfigDiffSpec, parsedToml: any, maps: ConfigDiffMaps, extra?: any): Record<string, any>;
|
|
1085
1204
|
/** The same projection for a SERVER entity — see `hashRemoteConfigForDiff`. */
|
|
1086
1205
|
export declare function projectRemoteConfig(spec: ConfigDiffSpec, record: any, maps: ConfigDiffMaps, extra?: any): Record<string, any>;
|
|
1206
|
+
/**
|
|
1207
|
+
* BOTH sides of one comparison, with the file's unmanaged keys reconciled
|
|
1208
|
+
* (#2880) — the one place `spec.unmanaged` is honored, so `config diff` and
|
|
1209
|
+
* `config push` cannot read the same file differently.
|
|
1210
|
+
*/
|
|
1211
|
+
export declare function projectConfigPair(spec: ConfigDiffSpec, localParsed: any, remoteRecord: any, maps: ConfigDiffMaps, extra?: any, localExtra?: any): {
|
|
1212
|
+
local: Record<string, any>;
|
|
1213
|
+
remote: Record<string, any>;
|
|
1214
|
+
};
|
|
1215
|
+
/**
|
|
1216
|
+
* One `config diff` row's content verdict for a registered type (#2880).
|
|
1217
|
+
*
|
|
1218
|
+
* The three outcomes the per-type blocks were each spelling out by hand:
|
|
1219
|
+
* equal (`exists`), different (`modified`, framed as a preview of
|
|
1220
|
+
* `config pull`), and "could not tell" — a missing record or a comparison that
|
|
1221
|
+
* threw, which degrades THIS row and never the whole diff. Written once so a
|
|
1222
|
+
* type joining the comparator cannot accidentally report a fourth thing.
|
|
1223
|
+
*
|
|
1224
|
+
* The two `extra` arguments are the same split `decidePushForConfig` makes:
|
|
1225
|
+
* `extra` carries what only the SERVER side has (a database type's operation
|
|
1226
|
+
* rows), while `localExtra` is context BOTH sides read a value through — a test
|
|
1227
|
+
* case's id→name lookups. Giving the local side nothing was a silent
|
|
1228
|
+
* mistranslation: a sidecar pinned by a resolvable `configId` projected the id
|
|
1229
|
+
* while the server's projection resolved it to the name, so an untouched file
|
|
1230
|
+
* reported Modified in diff and drifted in push.
|
|
1231
|
+
*/
|
|
1232
|
+
export declare function compareLocalToRemote(spec: ConfigDiffSpec, localParsed: any, remoteRecord: any, maps?: ConfigDiffMaps, extra?: any, localExtra?: any): {
|
|
1233
|
+
status: string;
|
|
1234
|
+
hint?: string;
|
|
1235
|
+
};
|
|
1087
1236
|
/** Hash a LOCAL config file's definition-projected field set (#2644). */
|
|
1088
|
-
export declare function hashLocalConfigForDiff(spec: ConfigDiffSpec, parsedToml: any, maps: ConfigDiffMaps): string;
|
|
1237
|
+
export declare function hashLocalConfigForDiff(spec: ConfigDiffSpec, parsedToml: any, maps: ConfigDiffMaps, extra?: any): string;
|
|
1089
1238
|
/**
|
|
1090
1239
|
* Hash a SERVER entity the same way, by serializing it into the file `sync
|
|
1091
1240
|
* pull` would write and hashing that — so the two sides are normalized
|
|
@@ -1105,10 +1254,13 @@ export interface PushFieldDiff {
|
|
|
1105
1254
|
/**
|
|
1106
1255
|
* What `config push` should do with one converted-type file, and why.
|
|
1107
1256
|
*
|
|
1108
|
-
* `create`/`skip`/`update` are the applying outcomes; `drift`, `conflict
|
|
1109
|
-
* `immutable` are the
|
|
1257
|
+
* `create`/`skip`/`update` are the applying outcomes; `drift`, `conflict`,
|
|
1258
|
+
* `immutable` and `live-unavailable` are the four ways push declines to apply
|
|
1259
|
+
* and says so.
|
|
1110
1260
|
*/
|
|
1111
1261
|
export type PushGateOutcome = {
|
|
1262
|
+
action: "live-unavailable";
|
|
1263
|
+
} | {
|
|
1112
1264
|
action: "create";
|
|
1113
1265
|
} | {
|
|
1114
1266
|
action: "skip";
|
|
@@ -1116,7 +1268,7 @@ export type PushGateOutcome = {
|
|
|
1116
1268
|
remoteHash: string;
|
|
1117
1269
|
} | {
|
|
1118
1270
|
action: "update";
|
|
1119
|
-
direction: "local-edited" | "forced";
|
|
1271
|
+
direction: "local-edited" | "forced" | "adopt-untracked";
|
|
1120
1272
|
localHash: string;
|
|
1121
1273
|
remoteHash?: string;
|
|
1122
1274
|
expectedModifiedAt?: string;
|
|
@@ -1162,7 +1314,47 @@ export interface PushBaselineEntry {
|
|
|
1162
1314
|
*
|
|
1163
1315
|
* Declining is a first-class outcome here. `drift`, `conflict` and `immutable`
|
|
1164
1316
|
* each carry the field diff their report prints, and nothing is applied.
|
|
1165
|
-
|
|
1317
|
+
*
|
|
1318
|
+
* `adoptsUntrackedByKey` is the one exception a type can ask for, and it is
|
|
1319
|
+
* documented on the field: an object the manifest has never recorded is the
|
|
1320
|
+
* adopt its create path would have performed via a 409 (#1006/#2909), not a
|
|
1321
|
+
* difference to refuse.
|
|
1322
|
+
*/
|
|
1323
|
+
/**
|
|
1324
|
+
* What the DEGRADED gate does with one file — the path a converted type takes
|
|
1325
|
+
* when its live read failed (#2880 criterion 7).
|
|
1326
|
+
*
|
|
1327
|
+
* Falling back to the manifest's byte hash is right while there is one to fall
|
|
1328
|
+
* back to. Without one, `shouldPushFile(file, undefined)` answers "push it",
|
|
1329
|
+
* so a fresh checkout or a lost manifest turned "we could not read the server"
|
|
1330
|
+
* into "overwrite the server", unconditionally and silently — the one shape of
|
|
1331
|
+
* blind write this issue's direction attribution exists to prevent. So a
|
|
1332
|
+
* baseline-less update DECLINES: the resource is not written and the operator
|
|
1333
|
+
* is told which of `config pull` / `--force` clears it.
|
|
1334
|
+
*
|
|
1335
|
+
* A file the manifest does not name is declined for the same reason, and this
|
|
1336
|
+
* is the part that is easy to get wrong: an unrecorded file looks like a
|
|
1337
|
+
* create, but only LIVE STATE can say the server has nothing under that key —
|
|
1338
|
+
* and live state is exactly what this path could not read. The create that
|
|
1339
|
+
* follows is adopted by key on the 409 and re-issued as an update, so
|
|
1340
|
+
* "there is no recorded entity to overwrite" would have been a blind overwrite
|
|
1341
|
+
* of a resource this run never compared. Every section's degraded gate is
|
|
1342
|
+
* reached only when the live read failed, so there is no case here where
|
|
1343
|
+
* absence was proved.
|
|
1344
|
+
*/
|
|
1345
|
+
export type DegradedPushDecision = {
|
|
1346
|
+
action: "skip";
|
|
1347
|
+
} | {
|
|
1348
|
+
action: "send";
|
|
1349
|
+
} | {
|
|
1350
|
+
action: "decline";
|
|
1351
|
+
reason: "no-baseline";
|
|
1352
|
+
};
|
|
1353
|
+
export declare function decideDegradedPush(input: {
|
|
1354
|
+
force?: boolean;
|
|
1355
|
+
storedContentHash?: string;
|
|
1356
|
+
currentFileHash?: string;
|
|
1357
|
+
}): DegradedPushDecision;
|
|
1166
1358
|
export declare function decidePushForConfig(input: {
|
|
1167
1359
|
spec: ConfigDiffSpec;
|
|
1168
1360
|
maps: ConfigDiffMaps;
|
|
@@ -1177,7 +1369,30 @@ export declare function decidePushForConfig(input: {
|
|
|
1177
1369
|
entry?: PushBaselineEntry | null;
|
|
1178
1370
|
/** Byte hash of the local file — the legacy direction signal. */
|
|
1179
1371
|
localFileHash?: string;
|
|
1372
|
+
/**
|
|
1373
|
+
* Per-type context for the LOCAL projection (#2880) — a test case's id→name
|
|
1374
|
+
* lookups. Separate from `live.extra`, which carries remote-only rows (a
|
|
1375
|
+
* database type's operations) the local parse must never see.
|
|
1376
|
+
*/
|
|
1377
|
+
localExtra?: any;
|
|
1180
1378
|
force?: boolean;
|
|
1379
|
+
/**
|
|
1380
|
+
* Whether this type's push path ADOPTS an existing object by key (#2909).
|
|
1381
|
+
*
|
|
1382
|
+
* Opt-in, and it changes exactly one outcome: a live entity the manifest has
|
|
1383
|
+
* no row for at all. Prompts (#1006), and the other types whose create path
|
|
1384
|
+
* recovers a 409 through `adoptByKeyOnCreate409`, treat that as the adopt it
|
|
1385
|
+
* has always been — an object pushed from another slot, created out of band,
|
|
1386
|
+
* or orphaned by a push that aborted before recording it, which the local
|
|
1387
|
+
* file is the declared intent for. Without this the gate reads that same
|
|
1388
|
+
* shape as a difference it cannot attribute, refuses, and the create the
|
|
1389
|
+
* adopt guard recovers from is never even sent (the #2909 regression).
|
|
1390
|
+
*
|
|
1391
|
+
* The types #2731 converted leave it off: for them a never-synced file
|
|
1392
|
+
* differing from a same-named live entity IS the conflict they report, and
|
|
1393
|
+
* that contract is unchanged.
|
|
1394
|
+
*/
|
|
1395
|
+
adoptsUntrackedByKey?: boolean;
|
|
1181
1396
|
}): PushGateOutcome;
|
|
1182
1397
|
/**
|
|
1183
1398
|
* The identity a file may leave to its name, written back into the parsed
|
|
@@ -1761,6 +1976,23 @@ export declare function pushTestCasesForBlock(params: {
|
|
|
1761
1976
|
}>;
|
|
1762
1977
|
/** Where a rejected create/update/upload goes — it fails the push (#2731 B2). */
|
|
1763
1978
|
failures: ApplyFailure[];
|
|
1979
|
+
/**
|
|
1980
|
+
* What push DECLINED to apply (#2880): server drift, a conflict, a live read
|
|
1981
|
+
* that failed with no baseline. Reported exactly as every other converted
|
|
1982
|
+
* type reports it — the caller passes its `recordDeclined`.
|
|
1983
|
+
*/
|
|
1984
|
+
declined?: (type: string, key: string, outcome: Extract<PushGateOutcome, {
|
|
1985
|
+
action: "drift" | "conflict" | "immutable" | "live-unavailable";
|
|
1986
|
+
}>, storedModifiedAt?: string) => void;
|
|
1987
|
+
/** The id→name lookups the comparison reads a reference through (#2880). */
|
|
1988
|
+
lookupMaps?: TestCaseLookupMaps;
|
|
1989
|
+
/**
|
|
1990
|
+
* Re-derive `lookupMaps` from the caller's name→id maps (#2880). Called after
|
|
1991
|
+
* this case's references are resolved and before it is compared: resolution
|
|
1992
|
+
* is what LOADS a block's configs, so the comparison would otherwise read a
|
|
1993
|
+
* reference the same push just learned how to read.
|
|
1994
|
+
*/
|
|
1995
|
+
refreshLookupMaps?: () => void;
|
|
1764
1996
|
resolutionMaps?: PushResolutionMaps;
|
|
1765
1997
|
options?: {
|
|
1766
1998
|
force?: boolean;
|
|
@@ -1769,25 +2001,53 @@ export declare function pushTestCasesForBlock(params: {
|
|
|
1769
2001
|
skipped: number;
|
|
1770
2002
|
}>;
|
|
1771
2003
|
/**
|
|
1772
|
-
*
|
|
2004
|
+
* Which live test case each sidecar manages (#2880 behavior 16).
|
|
1773
2005
|
*
|
|
1774
|
-
*
|
|
1775
|
-
*
|
|
1776
|
-
*
|
|
2006
|
+
* The manifest id comes FIRST. A test case's slug is derived from its name, so
|
|
2007
|
+
* matching on the slug alone made a rename — in either direction — read as a
|
|
2008
|
+
* new local file plus a removable remote case: diff showed two rows for one
|
|
2009
|
+
* case, and push created a duplicate. `entities.testCases[stateKey].id` is
|
|
2010
|
+
* stable across every rename, so it is the identity, and the slug is only how
|
|
2011
|
+
* a sidecar the manifest has never seen is ADOPTED.
|
|
2012
|
+
*
|
|
2013
|
+
* Adoption derives its slugs exactly the way pull writes them — same slugify,
|
|
2014
|
+
* same collision suffixes, same listing order — so a `<slug>-2.toml` on disk IS
|
|
2015
|
+
* the second colliding name and is paired with it.
|
|
2016
|
+
*
|
|
2017
|
+
* One function for both commands, because a diff row and a push decision that
|
|
2018
|
+
* pair differently are two answers to the same question.
|
|
1777
2019
|
*/
|
|
1778
|
-
export declare function
|
|
1779
|
-
|
|
2020
|
+
export declare function pairTestCases(input: {
|
|
2021
|
+
blockType: string;
|
|
2022
|
+
blockKey: string;
|
|
1780
2023
|
localSlugs: string[];
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
}
|
|
2024
|
+
liveCases: any[];
|
|
2025
|
+
managed: Record<string, {
|
|
2026
|
+
id?: string;
|
|
2027
|
+
}> | undefined;
|
|
2028
|
+
}): {
|
|
2029
|
+
bySlug: Map<string, any>;
|
|
2030
|
+
localOnly: string[];
|
|
2031
|
+
remoteOnly: any[];
|
|
2032
|
+
};
|
|
1785
2033
|
/**
|
|
1786
2034
|
* `config diff` for one block's test-case sidecar (#2769). A failed listing is
|
|
1787
2035
|
* an OUTCOME the caller reports as "not compared" — the old helper returned
|
|
1788
2036
|
* silently, so a block whose tests could not be fetched simply disappeared from
|
|
1789
2037
|
* a report that still read as exhaustive.
|
|
1790
2038
|
*/
|
|
2039
|
+
export interface TestCaseDiffRow {
|
|
2040
|
+
blockType: string;
|
|
2041
|
+
blockKey: string;
|
|
2042
|
+
slug: string;
|
|
2043
|
+
status: string;
|
|
2044
|
+
/** What a validation error or a degraded comparison has to say. */
|
|
2045
|
+
hint?: string;
|
|
2046
|
+
/** Attachments push would upload from this sidecar (#2880 behavior 19). */
|
|
2047
|
+
attachmentUploads?: string[];
|
|
2048
|
+
/** Managed attachments `push --prune` would delete (never a plain push). */
|
|
2049
|
+
attachmentDeletions?: string[];
|
|
2050
|
+
}
|
|
1791
2051
|
export declare function compareTestCasesForBlock(params: {
|
|
1792
2052
|
client: ApiClient;
|
|
1793
2053
|
appId: string;
|
|
@@ -1795,14 +2055,12 @@ export declare function compareTestCasesForBlock(params: {
|
|
|
1795
2055
|
blockId: string;
|
|
1796
2056
|
blockKey: string;
|
|
1797
2057
|
configDir: string;
|
|
2058
|
+
/** The manifest — identity, and the attachment hashes the plan reads. */
|
|
2059
|
+
syncState?: SyncState | null;
|
|
2060
|
+
lookupMaps?: TestCaseLookupMaps;
|
|
1798
2061
|
}): Promise<{
|
|
1799
2062
|
ok: true;
|
|
1800
|
-
rows:
|
|
1801
|
-
blockType: string;
|
|
1802
|
-
blockKey: string;
|
|
1803
|
-
slug: string;
|
|
1804
|
-
status: string;
|
|
1805
|
-
}>;
|
|
2063
|
+
rows: TestCaseDiffRow[];
|
|
1806
2064
|
} | {
|
|
1807
2065
|
ok: false;
|
|
1808
2066
|
}>;
|
|
@@ -1814,6 +2072,17 @@ export declare function compareTestCasesForBlock(params: {
|
|
|
1814
2072
|
* on the way to it, pushing a test case that tested something else.
|
|
1815
2073
|
*/
|
|
1816
2074
|
export declare function testCaseSidecarErrors(filePath: string, tomlData: any): string[];
|
|
2075
|
+
/**
|
|
2076
|
+
* EVERY reason `config push` refuses one test-case sidecar (#2880 criterion 1).
|
|
2077
|
+
*
|
|
2078
|
+
* The four checks the push preflight ran inline: the document's shape, the keys
|
|
2079
|
+
* the definition recognizes, the declared type of each value, and the sidecar's
|
|
2080
|
+
* own authoring rules (#2769). Collected in one place because `config diff`
|
|
2081
|
+
* runs the identical list — it used to run only the last of the four, so a
|
|
2082
|
+
* sidecar with an unknown key or a mistyped ordinary field was reported Synced
|
|
2083
|
+
* by the command whose whole promise is that a Synced file pushes.
|
|
2084
|
+
*/
|
|
2085
|
+
export declare function testCaseSidecarPreflightErrors(filePath: string, tomlData: any): string[];
|
|
1817
2086
|
/** A managed test case whose authored file is gone (#2769). */
|
|
1818
2087
|
export interface TestCaseDeletionCandidate {
|
|
1819
2088
|
stateKey: string;
|