@oimlsmart/platform-server 0.2.13 → 0.2.15
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/package.json +1 -1
- package/src/store/d1.ts +15 -1
- package/src/store/sqlite/entities.ts +13 -2
- package/src/store/sqlite.ts +3 -3
- package/src/store.ts +28 -2
- package/src/vocab/permissions.ts +6 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oimlsmart/platform-server",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.15",
|
|
4
4
|
"description": "The OIML SMART platform server kernel: the store seam (ServerStore + the D1 and SQLite implementations), the canonical D1 migration set both deployments apply, the instance profile, the mailer, the RBAC map, the OIDC/OAuth client cones, and the shared role/permission vocabulary. Consumed by the smart monorepo (browser/) and the identity service.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
package/src/store/d1.ts
CHANGED
|
@@ -47,6 +47,7 @@ import {
|
|
|
47
47
|
type EmailChangeToken,
|
|
48
48
|
type EnrollmentToken,
|
|
49
49
|
type EntityChange,
|
|
50
|
+
type EntityListOptions,
|
|
50
51
|
type EntityRow,
|
|
51
52
|
type EntityWriteInput,
|
|
52
53
|
type EventEntityKey,
|
|
@@ -3542,7 +3543,7 @@ export class D1ServerStore implements ServerStore {
|
|
|
3542
3543
|
|
|
3543
3544
|
// ── the workflow entity store + change journal ───────────────────
|
|
3544
3545
|
|
|
3545
|
-
async listEntities(store: string): Promise<EntityRow[]> {
|
|
3546
|
+
async listEntities(store: string, options?: EntityListOptions): Promise<EntityRow[]> {
|
|
3546
3547
|
// The ORDER BY is the seam's contract (the 0.2.3 pin): the answer
|
|
3547
3548
|
// arrives in (org_id, rowid) order — the read's observable order
|
|
3548
3549
|
// since migration 0001, when the planner walked
|
|
@@ -3551,6 +3552,19 @@ export class D1ServerStore implements ServerStore {
|
|
|
3551
3552
|
// insertion (the smart app's render-baseline red, oimlsmart/smart
|
|
3552
3553
|
// PR #264) — a list read's order is a consumer-visible contract,
|
|
3553
3554
|
// never the planner's pick.
|
|
3555
|
+
// options.orgId narrows the candidate set (the seam's EntityListOptions
|
|
3556
|
+
// — the portal-load audit's R3-fix3): the kept groups (NULL stamps,
|
|
3557
|
+
// then the named org) are the two lowest org_id buckets, so the
|
|
3558
|
+
// filtered ORDER BY is the unfiltered order's restriction to the
|
|
3559
|
+
// candidates — a gate-driven projection of either answer is
|
|
3560
|
+
// byte-identical.
|
|
3561
|
+
if (options?.orgId) {
|
|
3562
|
+
const res = await this.stmt(
|
|
3563
|
+
'SELECT store, id, org_id, data, updated_at FROM entities WHERE store = ? AND (org_id = ? OR org_id IS NULL) ORDER BY org_id, rowid',
|
|
3564
|
+
store, options.orgId,
|
|
3565
|
+
).all<EntityRow>()
|
|
3566
|
+
return res.results
|
|
3567
|
+
}
|
|
3554
3568
|
const res = await this.stmt(
|
|
3555
3569
|
'SELECT store, id, org_id, data, updated_at FROM entities WHERE store = ? ORDER BY org_id, rowid', store,
|
|
3556
3570
|
).all<EntityRow>()
|
|
@@ -18,7 +18,7 @@ import { PUT_ENTITIES_CHUNK, orgIdOf } from '../../store'
|
|
|
18
18
|
export type { EntityRow, EntityChange } from '../../store'
|
|
19
19
|
export { ORG_FIELDS, CATALOG_STORES, orgIdOf } from '../../store'
|
|
20
20
|
|
|
21
|
-
import type { EntityRow, EntityChange, EntityWriteInput, JournalAppend } from '../../store'
|
|
21
|
+
import type { EntityRow, EntityChange, EntityListOptions, EntityWriteInput, JournalAppend } from '../../store'
|
|
22
22
|
|
|
23
23
|
/** The journal fan-out's ISOLATE-scope registry (the seam's
|
|
24
24
|
* onJournalAppend, the D1 half's twin): one module-scope set — a
|
|
@@ -69,12 +69,23 @@ const CERTIFICATE_NUMBER_SQL = `SELECT store, id, org_id, data, updated_at
|
|
|
69
69
|
AND json_extract(CASE WHEN json_valid(data) THEN data ELSE '{}' END, '$.certificate_number') COLLATE NOCASE = ?
|
|
70
70
|
ORDER BY org_id, rowid`
|
|
71
71
|
|
|
72
|
-
export function listEntities(store: string): EntityRow[] {
|
|
72
|
+
export function listEntities(store: string, options?: EntityListOptions): EntityRow[] {
|
|
73
73
|
// The ORDER BY is the seam's contract (the 0.2.3 pin, the D1 half's
|
|
74
74
|
// twin): (org_id, rowid) — the read's observable order since
|
|
75
75
|
// migration 0001's idx_entities_store_org walk, made planner-proof
|
|
76
76
|
// when migration 0023's expression index offered a second
|
|
77
77
|
// store-prefixed plan.
|
|
78
|
+
// options.orgId narrows the candidate set (the seam's EntityListOptions
|
|
79
|
+
// — the portal-load audit's R3-fix3): the kept groups (NULL stamps,
|
|
80
|
+
// then the named org) are the two lowest org_id buckets, so the
|
|
81
|
+
// filtered ORDER BY is the unfiltered order's restriction to the
|
|
82
|
+
// candidates — a gate-driven projection of either answer is
|
|
83
|
+
// byte-identical.
|
|
84
|
+
if (options?.orgId) {
|
|
85
|
+
return getDb()
|
|
86
|
+
.prepare('SELECT store, id, org_id, data, updated_at FROM entities WHERE store = ? AND (org_id = ? OR org_id IS NULL) ORDER BY org_id, rowid')
|
|
87
|
+
.all(store, options.orgId) as EntityRow[]
|
|
88
|
+
}
|
|
78
89
|
return getDb()
|
|
79
90
|
.prepare('SELECT store, id, org_id, data, updated_at FROM entities WHERE store = ? ORDER BY org_id, rowid')
|
|
80
91
|
.all(store) as EntityRow[]
|
package/src/store/sqlite.ts
CHANGED
|
@@ -194,7 +194,7 @@ import {
|
|
|
194
194
|
updateOpAccount,
|
|
195
195
|
updateUserName,
|
|
196
196
|
} from './sqlite/op-accounts-store'
|
|
197
|
-
import { installStore, type AccountEmail, type AddAccountEmailResult, type AuthUserPayload, type CertificateHolderClaim, type CertificateHolderOrg, type CompleteEmailChangeResult, type CompleteEnrollmentResult, type ConsumeOidcRefreshTokenResult, type EmailChangeToken, type EnrollmentToken, type EntityChange, type EntityRow, type EntityWriteInput, type EventEntityKey, type EventKeyFilter, type EventWriteInput, type FederationPeer, type IdentityApproval, type IdentityLink, type IdentityProvider, type InstrumentRegistration, type InstrumentRegistrationLifecycle, type InstrumentRegistrationScopeStatus, type InstrumentRegistrationWriteInput, type JournalAppend, type NotifyDelivery, type NotifyDeliveryStatus, type NotifyEntityMute, type NotifyInboxState, type NotifyPreferences, type NotifyRule, type OAuthInitialAssignment, type OidcAccessToken, type OidcAuthorization, type OidcClient, type OidcClientLaunch, type OidcCode, type OidcConsentGrant, type OidcKeyRow, type OidcRefreshToken, type OpAccountErasure, type OpClientRoleAssignment, type OpLiveSession, type OrgJoinRequest, type OrgMembership, type OrgMembershipState, type OrgRegistryContact, type OrgRegistryOrg, type OrgRegistryState, type PersonalAccessToken, type PlatformEvent, type ServerStore, type SessionView, type SsoSignInState, type UserAdminRow, type AdvanceCounterResult, type MfaPending, type RecoveryCodeState, type TotpSecret, type WebauthnChallenge, type WebauthnCredential } from '../store'
|
|
197
|
+
import { installStore, type AccountEmail, type AddAccountEmailResult, type AuthUserPayload, type CertificateHolderClaim, type CertificateHolderOrg, type CompleteEmailChangeResult, type CompleteEnrollmentResult, type ConsumeOidcRefreshTokenResult, type EmailChangeToken, type EnrollmentToken, type EntityChange, type EntityListOptions, type EntityRow, type EntityWriteInput, type EventEntityKey, type EventKeyFilter, type EventWriteInput, type FederationPeer, type IdentityApproval, type IdentityLink, type IdentityProvider, type InstrumentRegistration, type InstrumentRegistrationLifecycle, type InstrumentRegistrationScopeStatus, type InstrumentRegistrationWriteInput, type JournalAppend, type NotifyDelivery, type NotifyDeliveryStatus, type NotifyEntityMute, type NotifyInboxState, type NotifyPreferences, type NotifyRule, type OAuthInitialAssignment, type OidcAccessToken, type OidcAuthorization, type OidcClient, type OidcClientLaunch, type OidcCode, type OidcConsentGrant, type OidcKeyRow, type OidcRefreshToken, type OpAccountErasure, type OpClientRoleAssignment, type OpLiveSession, type OrgJoinRequest, type OrgMembership, type OrgMembershipState, type OrgRegistryContact, type OrgRegistryOrg, type OrgRegistryState, type PersonalAccessToken, type PlatformEvent, type ServerStore, type SessionView, type SsoSignInState, type UserAdminRow, type AdvanceCounterResult, type MfaPending, type RecoveryCodeState, type TotpSecret, type WebauthnChallenge, type WebauthnCredential } from '../store'
|
|
198
198
|
import {
|
|
199
199
|
advanceWebauthnCounter,
|
|
200
200
|
consumeMfaPending,
|
|
@@ -1015,8 +1015,8 @@ export function createSqliteServerStore(): ServerStore {
|
|
|
1015
1015
|
},
|
|
1016
1016
|
|
|
1017
1017
|
// ── the workflow entity store + change journal ──
|
|
1018
|
-
async listEntities(store: string): Promise<EntityRow[]> {
|
|
1019
|
-
return listEntities(store)
|
|
1018
|
+
async listEntities(store: string, options?: EntityListOptions): Promise<EntityRow[]> {
|
|
1019
|
+
return listEntities(store, options)
|
|
1020
1020
|
},
|
|
1021
1021
|
async getEntity(store: string, id: string): Promise<EntityRow | undefined> {
|
|
1022
1022
|
return getEntity(store, id)
|
package/src/store.ts
CHANGED
|
@@ -138,6 +138,29 @@ export interface EntityRow {
|
|
|
138
138
|
updated_at: string
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
+
/** listEntities' candidate narrowing (the 2026-09-01 portal-load audit,
|
|
142
|
+
* R3-fix3): the entities table carries an indexed org_id column
|
|
143
|
+
* (idx_entities_store_org, migration 0001) that reads never used —
|
|
144
|
+
* every list scanned the whole store and left the org cone to the
|
|
145
|
+
* consumer's per-row gate. */
|
|
146
|
+
export interface EntityListOptions {
|
|
147
|
+
/** Narrow the CANDIDATE set to the rows stamped under this org (org_id
|
|
148
|
+
* = the value — orgIdOf's first-org-field stamp, or the writer's own
|
|
149
|
+
* org on the stamping write paths) PLUS the unstamped rows (org_id IS
|
|
150
|
+
* NULL — a stamp-less row is never excluded: the stamp's absence says
|
|
151
|
+
* nothing about the row's cone). A candidate filter, NEVER a
|
|
152
|
+
* visibility decision: which rows of the narrowed set answer is the
|
|
153
|
+
* consumer's in-memory gate, authoritative as before. The consumer
|
|
154
|
+
* enables the filter only where its gate provably denies every row
|
|
155
|
+
* the filter drops (the per-store verification rides the consumer's
|
|
156
|
+
* change — org_id stamps the FIRST org field, so multi-org-field
|
|
157
|
+
* stores are never narrowed). The answer keeps the seam's declared
|
|
158
|
+
* order: (org_id, rowid) over the kept rows — the unfiltered order's
|
|
159
|
+
* restriction to the candidate set, so a gate-driven projection of
|
|
160
|
+
* either answer is byte-identical. */
|
|
161
|
+
orgId?: string
|
|
162
|
+
}
|
|
163
|
+
|
|
141
164
|
// ── federation peers (TODO.federation/04) ────────────────────────────
|
|
142
165
|
|
|
143
166
|
/** A pinned federation peer: the counterparty instance's descriptor,
|
|
@@ -2485,8 +2508,11 @@ export interface ServerStore {
|
|
|
2485
2508
|
* first, then by org id, then by insertion). Both backends spell the
|
|
2486
2509
|
* ORDER BY explicitly — a list read's order is a consumer-visible
|
|
2487
2510
|
* contract, never the planner's pick (the 0.2.3 pin, after
|
|
2488
|
-
* migration 0023's expression index flipped the unnamed walk).
|
|
2489
|
-
|
|
2511
|
+
* migration 0023's expression index flipped the unnamed walk).
|
|
2512
|
+
* `options.orgId` narrows the candidate set in SQL (EntityListOptions
|
|
2513
|
+
* — the portal-load audit's R3-fix3); the narrowed answer is the same
|
|
2514
|
+
* order's restriction to the candidates. */
|
|
2515
|
+
listEntities(store: string, options?: EntityListOptions): Promise<EntityRow[]>
|
|
2490
2516
|
getEntity(store: string, id: string): Promise<EntityRow | undefined>
|
|
2491
2517
|
/** The public register's certificate-number lookup (the verify
|
|
2492
2518
|
* surfaces' keyed read — the 2026-09-07 performance audit's
|
package/src/vocab/permissions.ts
CHANGED
|
@@ -420,6 +420,12 @@ export const CREATION_PERMISSIONS: Record<string, readonly ActionPermission[]> =
|
|
|
420
420
|
// document list is maintained by the certificate's issuers.
|
|
421
421
|
operatedSchemes: ['instance.settings'],
|
|
422
422
|
certificateDocumentLists: ['certificate.issue'],
|
|
423
|
+
// Registering a participant is the scheme operator's named act (the
|
|
424
|
+
// 2026-09-15 direct-registration posture: participation cases are
|
|
425
|
+
// CREATED active by the Secretariat — the B 18:2025 §11 RC/MC ballot
|
|
426
|
+
// pipeline is modelled, not operated). The organ split holds: the MC
|
|
427
|
+
// decides (suspension/reinstatement), the Secretariat administers.
|
|
428
|
+
participantApplications: ['participants.manage'],
|
|
423
429
|
}
|
|
424
430
|
|
|
425
431
|
/** FIELD guards: a field going unset → set is the act (the test
|